linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
@ 2020-09-01  2:59 Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci Badhri Jagan Sridharan
                   ` (16 more replies)
  0 siblings, 17 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

First of all apologies for mixing up the patch version as noted by
Heikki and Greg. All of them were v1's but since I was manually adding
the version numbers I mixed them up. Using the --reroll-count option
now. Updating the patch version to v6 (highest version number in the
previous patchset + 1) to avoid confusion.

I also rebased on to off of the recent usb-next tip:
5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
Which had the following changes causing merge conflict:
3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart

Addressed comments from Heikki and Randy which have described in the
individual commit's change history as well.

Badhri Jagan Sridharan (14):
  usb: typec: tcpci: Add register definitions to tcpci
  usb: typec: tcpci: Add support when hidden tx registers are
    inaccessible
  usb: typec: tcpci: update ROLE_CONTROL for DRP
  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: Implement enabling Auto Discharge disconnect support
  usb: typec: tcpci: Implement Auto discharge disconnect callbacks
  usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold

 .../bindings/connector/usb-connector.txt      | 128 ++++
 .../devicetree/bindings/usb/maxim,tcpci.txt   |  44 ++
 drivers/usb/typec/tcpm/Kconfig                |   5 +
 drivers/usb/typec/tcpm/Makefile               |  13 +-
 drivers/usb/typec/tcpm/tcpci.c                | 146 ++++-
 drivers/usb/typec/tcpm/tcpci.h                |  43 ++
 drivers/usb/typec/tcpm/tcpci_maxim.c          | 564 ++++++++++++++++++
 drivers/usb/typec/tcpm/tcpm.c                 | 291 ++++++++-
 include/dt-bindings/usb/pd.h                  |  10 +
 include/linux/usb/pd.h                        |  19 +-
 include/linux/usb/tcpm.h                      |  24 +-
 include/linux/usb/typec.h                     |  13 +
 12 files changed, 1266 insertions(+), 34 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/connector/usb-connector.txt
 create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.txt
 create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c


base-commit: 5fedf0d295d3ef69fd85fdee4cb68fd3756b54c2
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:10   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible Badhri Jagan Sridharan
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Add register definitions to trap extended alerts.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Change history:
- No code changes. Update version from v1 to v6 to avoid confusion
---
 drivers/usb/typec/tcpm/tcpci.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 11c36d086c86..fd26ca35814c 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -16,6 +16,7 @@
 #define TCPC_PD_INT_REV			0xa
 
 #define TCPC_ALERT			0x10
+#define TCPC_ALERT_EXTENDED_STATUS	BIT(13)
 #define TCPC_ALERT_VBUS_DISCNCT		BIT(11)
 #define TCPC_ALERT_RX_BUF_OVF		BIT(10)
 #define TCPC_ALERT_FAULT		BIT(9)
@@ -32,6 +33,10 @@
 #define TCPC_ALERT_MASK			0x12
 #define TCPC_POWER_STATUS_MASK		0x14
 #define TCPC_FAULT_STATUS_MASK		0x15
+
+#define TCPC_EXTENDED_STATUS_MASK		0x16
+#define TCPC_EXTENDED_STATUS_MASK_VSAFE0V	BIT(0)
+
 #define TCPC_CONFIG_STD_OUTPUT		0x18
 
 #define TCPC_TCPC_CTRL			0x19
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:11   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP Badhri Jagan Sridharan
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

TCPCI spec forbids direct access of TX_BUF_BYTE_x register.
The existing version of tcpci driver assumes that those registers
are directly addressible. Add support for tcpci chips which do
not support direct access to TX_BUF_BYTE_x registers. TX_BUF_BYTE_x
can only be accessed by I2C_WRITE_BYTE_COUNT.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
 - Refactored the code to check for TX_BUF_BYTE_x_hidden as suggested by
   Heikki.
 - Not formatting the tcpci_pd_transmit to follow the 100 character/line
   limit as suggested by Heikki. (Should be a separate change).
 - Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 46 ++++++++++++++++++++++++++--------
 drivers/usb/typec/tcpm/tcpci.h |  8 ++++++
 2 files changed, 43 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index bd80e03b2b6f..7d36d5e2d3f7 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -330,23 +330,47 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
 	int ret;
 
 	cnt = msg ? pd_header_cnt(header) * 4 : 0;
-	ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
-	if (ret < 0)
-		return ret;
+	/**
+	 * TCPCI spec forbids direct access of TCPC_TX_DATA.
+	 * But, since some of the chipsets offer this capability,
+	 * it's fair to support both.
+	 */
+	if (tcpci->data->TX_BUF_BYTE_x_hidden) {
+		u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,};
+		u8 pos = 0;
 
-	ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
-	if (ret < 0)
-		return ret;
+		/* Payload + header + TCPC_TX_BYTE_CNT */
+		buf[pos++] = cnt + 2;
+
+		if (msg)
+			memcpy(&buf[pos], &msg->header, sizeof(msg->header));
+
+		pos += sizeof(header);
 
-	if (cnt > 0) {
-		ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
-				       &msg->payload, cnt);
+		if (cnt > 0)
+			memcpy(&buf[pos], msg->payload, cnt);
+
+		pos += cnt;
+		ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos);
+		if (ret < 0)
+			return ret;
+	} else {
+		ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
 		if (ret < 0)
 			return ret;
+
+		ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
+		if (ret < 0)
+			return ret;
+
+		if (cnt > 0) {
+			ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt);
+			if (ret < 0)
+				return ret;
+		}
 	}
 
-	reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
-		(type << TCPC_TRANSMIT_TYPE_SHIFT);
+	reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
 	ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index fd26ca35814c..cf9d8b63adcb 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -128,9 +128,17 @@
 #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG		0x76
 #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG		0x78
 
+/* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */
+#define TCPC_TRANSMIT_BUFFER_MAX_LEN		31
+
+/*
+ * @TX_BUF_BYTE_x_hidden
+ *		optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
+ */
 struct tcpci;
 struct tcpci_data {
 	struct regmap *regmap;
+	unsigned char TX_BUF_BYTE_x_hidden:1;
 	int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
 	int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
 			 bool enable);
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:12   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference Badhri Jagan Sridharan
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

ROLE_CONTROL register would not have the actual CC terminations
unless the port does not set ROLE_CONTROL.DRP. For DRP ports,
CC_STATUS.cc1/cc2 indicates the final terminations applied
when TCPC enters potential_connect_as_source/_sink.
For DRP ports, infer port role from CC_STATUS and set corresponding
CC terminations before setting the orientation.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
 - Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 37 +++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 7d36d5e2d3f7..7d9491ba62fb 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -191,12 +191,47 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
 	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
 	unsigned int reg;
 	int ret;
+	enum typec_cc_status cc1, cc2;
 
-	/* Keep the disconnect cc line open */
+	/* Obtain Rp setting from role control */
 	ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &reg);
 	if (ret < 0)
 		return ret;
 
+	ret = tcpci_get_cc(tcpc, &cc1, &cc2);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * When port has drp toggling enabled, ROLE_CONTROL would only have the initial
+	 * terminations for the toggling and does not indicate the final cc
+	 * terminations when ConnectionResult is 0 i.e. drp toggling stops and
+	 * the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the
+	 * terminations seen. The port role is then used to set the cc terminations.
+	 */
+	if (reg & TCPC_ROLE_CTRL_DRP) {
+		/* Disable DRP for the OPEN setting to take effect */
+		reg = reg & ~TCPC_ROLE_CTRL_DRP;
+
+		if (polarity == TYPEC_POLARITY_CC2) {
+			reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
+			/* Local port is source */
+			if (cc2 == TYPEC_CC_RD)
+				/* Role control would have the Rp setting when DRP was enabled */
+				reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
+			else
+				reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+		} else {
+			reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
+			/* Local port is source */
+			if (cc1 == TYPEC_CC_RD)
+				/* Role control would have the Rp setting when DRP was enabled */
+				reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
+			else
+				reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
+		}
+	}
+
 	if (polarity == TYPEC_POLARITY_CC2)
 		reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
 	else
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (2 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:16   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback Badhri Jagan Sridharan
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Allow chip level drivers to retrieve reference to tcpm_port.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Change since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 6 ++++++
 drivers/usb/typec/tcpm/tcpci.h | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 7d9491ba62fb..b960fe5a0f28 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -38,6 +38,12 @@ struct tcpci_chip {
 	struct tcpci_data data;
 };
 
+struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci)
+{
+	return tcpci->port;
+}
+EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port);
+
 static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
 {
 	return container_of(tcpc, struct tcpci, tcpc);
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index cf9d8b63adcb..04c49a0b0368 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -150,4 +150,6 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
 void tcpci_unregister_port(struct tcpci *tcpci);
 irqreturn_t tcpci_irq(struct tcpci *tcpci);
 
+struct tcpm_port;
+struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci);
 #endif /* __LINUX_USB_TCPCI_H */
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (3 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:22   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 06/14] dt-bindings: usb: Maxim type-c controller device tree binding document Badhri Jagan Sridharan
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

set_vbus callback allows TCPC which are TCPCI based, however,
does not support turning on sink and source mode through
Command.SinkVbus and Command.SourceVbusDefaultVoltage.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 7 +++++++
 drivers/usb/typec/tcpm/tcpci.h | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index b960fe5a0f28..d6a6fac82d48 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -328,6 +328,13 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
 	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
 	int ret;
 
+	if (tcpci->data->set_vbus) {
+		ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink);
+		/* Bypass when ret > 0 */
+		if (ret != 0)
+			return ret < 0 ? ret : 0;
+	}
+
 	/* Disable both source and sink first before enabling anything */
 
 	if (!source) {
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 04c49a0b0368..4d441bdf24d5 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -144,6 +144,7 @@ struct tcpci_data {
 			 bool enable);
 	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);
 };
 
 struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 06/14] dt-bindings: usb: Maxim type-c controller device tree binding document
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (4 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver Badhri Jagan Sridharan
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

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

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 .../devicetree/bindings/usb/maxim,tcpci.txt   | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.txt

diff --git a/Documentation/devicetree/bindings/usb/maxim,tcpci.txt b/Documentation/devicetree/bindings/usb/maxim,tcpci.txt
new file mode 100644
index 000000000000..8a5b08e57b2d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/maxim,tcpci.txt
@@ -0,0 +1,44 @@
+Maxim TCPCI Type-C PD controller:
+---------------------------------
+
+Required properties:
+- compatible:       should be set maxim,tcpci:
+
+- reg:              0x25:the i2c slave address of typec port controller device.
+- interrupt-parent: the phandle to the interrupt controller which provides
+                    the interrupt.
+- usbpd,usbpd_int:  interrupt specification for tcpci alert.
+
+Required sub-node:
+- connector: The "usb-c-connector" attached to the tcpci chip, the bindings
+  of connector node are specified in
+  Documentation/devicetree/bindings/connector/usb-connector.txt
+
+maxtcpc: maxtcpc@25 {
+	status = "okay";
+	compatible = "maxim,tcpc";
+	reg = <0x25>;
+	interrupt-parent = <&gpa8>;
+	usbpd,usbpd_int = <&gpa8 2 GPIO_ACTIVE_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)
+		frs-typec-current = <FRS_5V_1P5A>;
+	};
+};
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (5 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 06/14] dt-bindings: usb: Maxim type-c controller device tree binding document Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:43   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 08/14] dt-bindings: connector: Add property to set initial current cap for FRS Badhri Jagan Sridharan
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Chip level TCPC driver for Maxim's TCPCI implementation.
This TCPC implementation does not support the following
commands: COMMAND.SinkVbus, COMMAND.SourceVbusDefaultVoltage,
COMMAND.SourceVbusHighVoltage. Instead the sinking and sourcing
from vbus is supported by writes to custom registers.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
- Removed setting USB_PSY and terminating description with period as
  suggested by Randy.
---
 drivers/usb/typec/tcpm/Kconfig       |   5 +
 drivers/usb/typec/tcpm/Makefile      |  13 +-
 drivers/usb/typec/tcpm/tcpci.h       |   1 +
 drivers/usb/typec/tcpm/tcpci_maxim.c | 474 +++++++++++++++++++++++++++
 4 files changed, 487 insertions(+), 6 deletions(-)
 create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c

diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index fa3f39336246..dd0d446a4613 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -27,6 +27,11 @@ config TYPEC_RT1711H
 	  Type-C Port Controller Manager to provide USB PD and USB
 	  Type-C functionalities.
 
+config TYPEC_TCPCI_MAXIM
+	tristate "Maxim TCPCI based Type-C chip driver"
+	help
+	  MAXIM TCPCI based Type-C chip driver.
+
 endif # TYPEC_TCPCI
 
 config TYPEC_FUSB302
diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
index a5ff6c8eb892..58d001cf0dd2 100644
--- a/drivers/usb/typec/tcpm/Makefile
+++ b/drivers/usb/typec/tcpm/Makefile
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_TYPEC_TCPM)	+= tcpm.o
-obj-$(CONFIG_TYPEC_FUSB302)	+= fusb302.o
-obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
-typec_wcove-y			:= wcove.o
-obj-$(CONFIG_TYPEC_TCPCI)	+= tcpci.o
-obj-$(CONFIG_TYPEC_RT1711H)	+= tcpci_rt1711h.o
+obj-$(CONFIG_TYPEC_TCPM)		+= tcpm.o
+obj-$(CONFIG_TYPEC_FUSB302)		+= fusb302.o
+obj-$(CONFIG_TYPEC_WCOVE)		+= typec_wcove.o
+typec_wcove-y				:= wcove.o
+obj-$(CONFIG_TYPEC_TCPCI)		+= tcpci.o
+obj-$(CONFIG_TYPEC_RT1711H)		+= tcpci_rt1711h.o
+obj-$(CONFIG_TYPEC_TCPCI_MAXIM)		+= tcpci_maxim.o
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 4d441bdf24d5..82f021a82456 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -109,6 +109,7 @@
 
 #define TCPC_RX_BYTE_CNT		0x30
 #define TCPC_RX_BUF_FRAME_TYPE		0x31
+#define TCPC_RX_BUF_FRAME_TYPE_SOP	0
 #define TCPC_RX_HDR			0x32
 #define TCPC_RX_DATA			0x34 /* through 0x4f */
 
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
new file mode 100644
index 000000000000..b61f290a8f96
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Google LLC
+ *
+ * MAXIM TCPCI based TCPC driver
+ */
+
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec.h>
+
+#include "tcpci.h"
+
+#define PD_ACTIVITY_TIMEOUT_MS				10000
+
+#define TCPC_VENDOR_ALERT				0x80
+
+#define TCPC_RECEIVE_BUFFER_COUNT_OFFSET		0
+#define TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET		1
+#define TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET		2
+
+/*
+ * LongMessage not supported, hence 32 bytes for buf to be read from RECEIVE_BUFFER.
+ * DEVICE_CAPABILITIES_2.LongMessage = 0, the value in READABLE_BYTE_COUNT reg shall be
+ * less than or equal to 31. Since, RECEIVE_BUFFER len = 31 + 1(READABLE_BYTE_COUNT).
+ */
+#define TCPC_RECEIVE_BUFFER_LEN				32
+
+#define MAX_BUCK_BOOST_SID				0x69
+#define MAX_BUCK_BOOST_OP				0xb9
+#define MAX_BUCK_BOOST_OFF				0
+#define MAX_BUCK_BOOST_SOURCE				0xa
+#define MAX_BUCK_BOOST_SINK				0x5
+
+struct max_tcpci_chip {
+	struct tcpci_data data;
+	struct tcpci *tcpci;
+	struct device *dev;
+	struct i2c_client *client;
+	struct tcpm_port *port;
+};
+
+static const struct regmap_range max_tcpci_tcpci_range[] = {
+	regmap_reg_range(0x00, 0x95)
+};
+
+const struct regmap_access_table max_tcpci_tcpci_write_table = {
+	.yes_ranges = max_tcpci_tcpci_range,
+	.n_yes_ranges = ARRAY_SIZE(max_tcpci_tcpci_range),
+};
+
+static const struct regmap_config max_tcpci_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x95,
+	.wr_table = &max_tcpci_tcpci_write_table,
+};
+
+static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata)
+{
+	return container_of(tdata, struct max_tcpci_chip, data);
+}
+
+static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
+{
+	return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
+}
+
+static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
+{
+	return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
+}
+
+static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
+{
+	return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
+}
+
+static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
+{
+	return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
+}
+
+static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
+{
+	u16 alert_mask = 0;
+	int ret;
+
+	ret = max_tcpci_write16(chip, TCPC_ALERT, 0xffff);
+	if (ret < 0) {
+		dev_err(chip->dev, "Error writing to TCPC_ALERT ret:%d\n", ret);
+		return;
+	}
+
+	ret = max_tcpci_write16(chip, TCPC_VENDOR_ALERT, 0xffff);
+	if (ret < 0) {
+		dev_err(chip->dev, "Error writing to TCPC_VENDOR_ALERT 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;
+
+	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);
+		return;
+	}
+
+	/* Enable vbus voltage monitoring and voltage alerts */
+	ret = max_tcpci_write8(chip, TCPC_POWER_CTRL, 0);
+	if (ret < 0) {
+		dev_err(chip->dev, "Error writing to TCPC_POWER_CTRL ret:%d\n", ret);
+		return;
+	}
+}
+
+static void process_rx(struct max_tcpci_chip *chip, u16 status)
+{
+	struct pd_message msg;
+	u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN];
+	int ret, payload_index;
+	u8 *rx_buf_ptr;
+
+	/*
+	 * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_x registers
+	 * plus one (for the RX_BUF_FRAME_TYPE) Table 4-36.
+	 * Read the count and frame type.
+	 */
+	ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, 2);
+	if (ret < 0) {
+		dev_err(chip->dev, "TCPC_RX_BYTE_CNT read failed ret:%d", ret);
+		return;
+	}
+
+	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) {
+		max_tcpci_write16(chip, 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 (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
+		dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d", count);
+		return;
+	}
+
+	/*
+	 * Read count + 1 as RX_BUF_BYTE_x is hidden and can only be read through
+	 * TCPC_RX_BYTE_CNT
+	 */
+	count += 1;
+	ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, count);
+	if (ret < 0) {
+		dev_err(chip->dev, "Error: TCPC_RX_BYTE_CNT read failed: %d", ret);
+		return;
+	}
+
+	rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
+	msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
+	rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
+	for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
+	     rx_buf_ptr += sizeof(msg.payload[0]))
+		msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);
+
+	/*
+	 * Read complete, clear RX status alert bit.
+	 * Clear overflow as well if set.
+	 */
+	ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
+				TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
+				TCPC_ALERT_RX_STATUS);
+	if (ret < 0)
+		return;
+
+	tcpm_pd_receive(chip->port, &msg);
+}
+
+static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink)
+{
+	struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+	u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE};
+	u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK};
+	u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF};
+	struct i2c_client *i2c = chip->client;
+	int ret;
+
+	struct i2c_msg msgs[] = {
+		{
+			.addr = MAX_BUCK_BOOST_SID,
+			.flags = i2c->flags & I2C_M_TEN,
+			.len = 2,
+			.buf = source ? buffer_source : sink ? buffer_sink : buffer_none,
+		},
+	};
+
+	if (source && sink) {
+		dev_err(chip->dev, "Both source and sink set\n");
+		return -EINVAL;
+	}
+
+	ret = i2c_transfer(i2c->adapter, msgs, 1);
+
+	return  ret < 0 ? ret : 1;
+}
+
+static void process_power_status(struct max_tcpci_chip *chip)
+{
+	u8 pwr_status;
+	int ret;
+
+	ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &pwr_status);
+	if (ret < 0)
+		return;
+
+	if (pwr_status == 0xff)
+		max_tcpci_init_regs(chip);
+	else
+		tcpm_vbus_change(chip->port);
+}
+
+static void process_tx(struct max_tcpci_chip *chip, u16 status)
+{
+	if (status & TCPC_ALERT_TX_SUCCESS)
+		tcpm_pd_transmit_complete(chip->port, TCPC_TX_SUCCESS);
+	else if (status & TCPC_ALERT_TX_DISCARDED)
+		tcpm_pd_transmit_complete(chip->port, TCPC_TX_DISCARDED);
+	else if (status & TCPC_ALERT_TX_FAILED)
+		tcpm_pd_transmit_complete(chip->port, TCPC_TX_FAILED);
+
+	/* Reinit regs as Hard reset sets them to default value */
+	if ((status & TCPC_ALERT_TX_SUCCESS) && (status & TCPC_ALERT_TX_FAILED))
+		max_tcpci_init_regs(chip);
+}
+
+static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
+{
+	u16 mask;
+	int ret;
+
+	/*
+	 * Clear alert status for everything except RX_STATUS, which shouldn't
+	 * be cleared until we have successfully retrieved message.
+	 */
+	if (status & ~TCPC_ALERT_RX_STATUS) {
+		mask = status & TCPC_ALERT_RX_BUF_OVF ?
+			status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
+			status & ~TCPC_ALERT_RX_STATUS;
+		ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
+		if (ret < 0) {
+			dev_err(chip->dev, "ALERT clear failed\n");
+			return ret;
+		}
+	}
+
+	if (status & TCPC_ALERT_RX_BUF_OVF && !(status & TCPC_ALERT_RX_STATUS)) {
+		ret = max_tcpci_write16(chip, TCPC_ALERT, (TCPC_ALERT_RX_STATUS |
+							  TCPC_ALERT_RX_BUF_OVF));
+		if (ret < 0) {
+			dev_err(chip->dev, "ALERT clear failed\n");
+			return ret;
+		}
+	}
+
+	if (status & TCPC_ALERT_RX_STATUS)
+		process_rx(chip, status);
+
+	if (status & TCPC_ALERT_TX_DISCARDED)
+		dev_info(chip->dev, "TX_DISCARDED");
+
+	if (status & TCPC_ALERT_VBUS_DISCNCT)
+		tcpm_vbus_change(chip->port);
+
+	if (status & TCPC_ALERT_CC_STATUS)
+		tcpm_cc_change(chip->port);
+
+	if (status & TCPC_ALERT_POWER_STATUS)
+		process_power_status(chip);
+
+	if (status & TCPC_ALERT_RX_HARD_RST) {
+		tcpm_pd_hard_reset(chip->port);
+		max_tcpci_init_regs(chip);
+	}
+
+	if (status & TCPC_ALERT_TX_SUCCESS || status & TCPC_ALERT_TX_DISCARDED || status &
+	    TCPC_ALERT_TX_FAILED)
+		process_tx(chip, status);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
+{
+	struct max_tcpci_chip *chip = dev_id;
+	u16 status;
+	irqreturn_t irq_return;
+	int ret;
+
+	if (!chip->port)
+		return IRQ_HANDLED;
+
+	ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
+	if (ret < 0) {
+		dev_err(chip->dev, "ALERT read failed\n");
+		return ret;
+	}
+	while (status) {
+		irq_return = _max_tcpci_irq(chip, status);
+		/* Do not return if the ALERT is already set. */
+		ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
+		if (ret < 0)
+			break;
+	}
+
+	return irq_return;
+}
+
+static irqreturn_t max_tcpci_isr(int irq, void *dev_id)
+{
+	struct max_tcpci_chip *chip = dev_id;
+
+	pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS);
+
+	if (!chip->port)
+		return IRQ_HANDLED;
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client)
+{
+	int ret, irq_gpio;
+
+	irq_gpio = of_get_named_gpio(client->dev.of_node, "usbpd,usbpd_int", 0);
+	client->irq = gpio_to_irq(irq_gpio);
+	if (!client->irq)
+		return -ENODEV;
+
+	ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
+					(IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
+					chip);
+
+	if (ret < 0)
+		return ret;
+
+	enable_irq_wake(client->irq);
+	return 0;
+}
+
+static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata,
+				    enum typec_cc_status cc)
+{
+	struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+
+	max_tcpci_init_regs(chip);
+
+	return 0;
+}
+
+static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
+{
+	/*
+	 * Generic TCPCI overwrites the regs once this driver initializes
+	 * them. Prevent this by returning -1.
+	 */
+	return -1;
+}
+
+static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)
+{
+	int ret;
+	struct max_tcpci_chip *chip;
+	u8 power_status;
+
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->client = client;
+	chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
+	if (IS_ERR(chip->data.regmap)) {
+		dev_err(&client->dev, "Regmap init failed\n");
+		return PTR_ERR(chip->data.regmap);
+	}
+
+	chip->dev = &client->dev;
+	i2c_set_clientdata(client, chip);
+
+	ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
+	if (ret < 0)
+		return ret;
+
+	if (power_status & TCPC_POWER_STATUS_UNINIT) {
+		dev_err(&client->dev, "TCPC not ready!");
+		return -EPROBE_DEFER;
+	}
+
+	/* Chip level tcpci callbacks */
+	chip->data.set_vbus = max_tcpci_set_vbus;
+	chip->data.start_drp_toggling = max_tcpci_start_toggling;
+	chip->data.TX_BUF_BYTE_x_hidden = true;
+	chip->data.init = tcpci_init;
+
+	max_tcpci_init_regs(chip);
+	chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
+	if (IS_ERR_OR_NULL(chip->tcpci)) {
+		dev_err(&client->dev, "TCPCI port registration failed");
+		ret = PTR_ERR(chip->tcpci);
+		return PTR_ERR(chip->tcpci);
+	}
+	chip->port = tcpci_get_tcpm_port(chip->tcpci);
+	ret = max_tcpci_init_alert(chip, client);
+	if (ret < 0)
+		goto unreg_port;
+
+	device_init_wakeup(chip->dev, true);
+	return 0;
+
+unreg_port:
+	tcpci_unregister_port(chip->tcpci);
+
+	return ret;
+}
+
+static int max_tcpci_remove(struct i2c_client *client)
+{
+	struct max_tcpci_chip *chip = i2c_get_clientdata(client);
+
+	if (!IS_ERR_OR_NULL(chip->tcpci))
+		tcpci_unregister_port(chip->tcpci);
+
+	return 0;
+}
+
+static const struct i2c_device_id max_tcpci_id[] = {
+	{ "maxtcpc", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max_tcpci_of_match[] = {
+	{ .compatible = "maxim,tcpc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, max_tcpci_of_match);
+#endif
+
+static struct i2c_driver max_tcpci_i2c_driver = {
+	.driver = {
+		.name = "maxtcpc",
+		.of_match_table = of_match_ptr(max_tcpci_of_match),
+	},
+	.probe = max_tcpci_probe,
+	.remove = max_tcpci_remove,
+	.id_table = max_tcpci_id,
+};
+module_i2c_driver(max_tcpci_i2c_driver);
+
+MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
+MODULE_DESCRIPTION("Maxim TCPCI based USB Type-C Port Controller Interface Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 08/14] dt-bindings: connector: Add property to set initial current cap for FRS
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (6 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS) Badhri Jagan Sridharan
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

This change adds frs-typec-current which allows setting the initial current
capability of the new source when vSafe5V is applied during PD3.0
sink Fast Role Swap.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 .../bindings/connector/usb-connector.txt      | 128 ++++++++++++++++++
 include/dt-bindings/usb/pd.h                  |  10 ++
 2 files changed, 138 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/connector/usb-connector.txt

diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
new file mode 100644
index 000000000000..e2f6e0f07d00
--- /dev/null
+++ b/Documentation/devicetree/bindings/connector/usb-connector.txt
@@ -0,0 +1,128 @@
+USB Connector
+=============
+
+USB connector node represents physical USB connector. It should be
+a child of USB interface controller.
+
+Required properties:
+- compatible: describes type of the connector, must be one of:
+    "usb-a-connector",
+    "usb-b-connector",
+    "usb-c-connector".
+
+Optional properties:
+- label: symbolic name for the connector,
+- type: size of the connector, should be specified in case of USB-A, USB-B
+  non-fullsize connectors: "mini", "micro".
+
+Optional properties for usb-c-connector:
+- power-role: should be one of "source", "sink" or "dual"(DRP) if typec
+  connector has power support.
+- try-power-role: preferred power role if "dual"(DRP) can support Try.SNK
+  or Try.SRC, should be "sink" for Try.SNK or "source" for Try.SRC.
+- data-role: should be one of "host", "device", "dual"(DRD) if typec
+  connector supports USB data.
+- frs-typec-current - Initial current capability of the new source when vSafe5V
+  is applied during PD3.0 Fast Role Swap. "Table 6-14 Fixed Supply PDO - Sink"
+  of "USB Power Delivery Specification Revision 3.0, Version 1.2" provides the
+  different power levels and "6.4.1.3.1.6 Fast Role Swap USB Type-C Current"
+  provides a detailed description of the field.
+  0: Fast role swap not supported
+  1: Default USB Power
+  2: 1.5A @ 5V
+  3: 3A @ 5V.
+
+Required properties for usb-c-connector with power delivery support:
+- source-pdos: An array of u32 with each entry providing supported power
+  source data object(PDO), the detailed bit definitions of PDO can be found
+  in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2
+  Source_Capabilities Message, the order of each entry(PDO) should follow
+  the PD spec chapter 6.4.1. Required for power source and power dual role.
+  User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO()
+  defined in dt-bindings/usb/pd.h.
+- sink-pdos: An array of u32 with each entry providing supported power
+  sink data object(PDO), the detailed bit definitions of PDO can be found
+  in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3
+  Sink Capabilities Message, the order of each entry(PDO) should follow
+  the PD spec chapter 6.4.1. Required for power sink and power dual role.
+  User can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined
+  in dt-bindings/usb/pd.h.
+- op-sink-microwatt: Sink required operating power in microwatt, if source
+  can't offer the power, Capability Mismatch is set. Required for power
+  sink and power dual role.
+
+Required nodes:
+- any data bus to the connector should be modeled using the OF graph bindings
+  specified in bindings/graph.txt, unless the bus is between parent node and
+  the connector. Since single connector can have multpile data buses every bus
+  has assigned OF graph port number as follows:
+    0: High Speed (HS), present in all connectors,
+    1: Super Speed (SS), present in SS capable connectors,
+    2: Sideband use (SBU), present in USB-C.
+
+Examples
+--------
+
+1. Micro-USB connector with HS lines routed via controller (MUIC):
+
+muic-max77843@66 {
+	...
+	usb_con: connector {
+		compatible = "usb-b-connector";
+		label = "micro-USB";
+		type = "micro";
+	};
+};
+
+2. USB-C connector attached to CC controller (s2mm005), HS lines routed
+to companion PMIC (max77865), SS lines to USB3 PHY and SBU to DisplayPort.
+DisplayPort video lines are routed to the connector via SS mux in USB3 PHY.
+
+ccic: s2mm005@33 {
+	...
+	usb_con: connector {
+		compatible = "usb-c-connector";
+		label = "USB-C";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				usb_con_hs: endpoint {
+					remote-endpoint = <&max77865_usbc_hs>;
+				};
+			};
+			port@1 {
+				reg = <1>;
+				usb_con_ss: endpoint {
+					remote-endpoint = <&usbdrd_phy_ss>;
+				};
+			};
+			port@2 {
+				reg = <2>;
+				usb_con_sbu: endpoint {
+					remote-endpoint = <&dp_aux>;
+				};
+			};
+		};
+	};
+};
+
+3. USB-C connector attached to a typec port controller(ptn5110), which has
+power delivery support and enables drp.
+
+typec: ptn5110@50 {
+	...
+	usb_con: connector {
+		compatible = "usb-c-connector";
+		label = "USB-C";
+		power-role = "dual";
+		try-power-role = "sink";
+		source-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)>;
+		sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)
+			     PDO_VAR(5000, 12000, 2000)>;
+		op-sink-microwatt = <10000000>;
+	};
+};
diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h
index 985f2bbd4d24..db1ad4532197 100644
--- a/include/dt-bindings/usb/pd.h
+++ b/include/dt-bindings/usb/pd.h
@@ -35,6 +35,16 @@
 
 #define VSAFE5V 5000 /* mv units */
 
+/*
+ * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
+ * Version 1.2"
+ * Initial current capability of the new source when vSafe5V is applied.
+ */
+#define FRS_NOT_SUPPORTED	0
+#define FRS_DEFAULT_POWER	1
+#define FRS_5V_1P5A		2
+#define FRS_5V_3A		3
+
 #define PDO_BATT_MAX_VOLT_SHIFT	20	/* 50mV units */
 #define PDO_BATT_MIN_VOLT_SHIFT	10	/* 50mV units */
 #define PDO_BATT_MAX_PWR_SHIFT	0	/* 250mW units */
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS)
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (7 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 08/14] dt-bindings: connector: Add property to set initial current cap for FRS Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 13:20   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS Badhri Jagan Sridharan
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

PD 3.0 spec defines a new mechanism for power role swap called
Fast role swap. This change enables TCPM to support FRS when
acting as sink.

Once the explicit contract is negotiated, sink port is
expected to query the source port for sink caps to
determine whether the source is FRS capable.
Bits 23 & 24 of fixed pdo of the sink caps from the source, when
set, indicates the current needed by the source when fast role
swap is in progress(Implicit contract phasae). 0 indicates that
the source does not support Fast Role Swap.

Upon receiving the FRS signal from the source,
TCPC(TCPM_FRS_EVENT) informs TCPM to start the Fast role swap sequence.

1. TCPM sends FRS PD message: FR_SWAP_SEND
2. If response is not received within the expiry of
   SenderResponseTimer, Error recovery is triggered.:
   FR_SWAP_SEND_TIMEOUT
3. Upon receipt of the accept message, TCPM waits for
   PSSourceOffTimer for PS_READY message from the partner:
   FR_SWAP_SNK_SRC_NEW_SINK_READY.

TCPC is expected to autonomously turn on vbus once the FRS
signal is received and vbus voltage falls below vsafe5v within
tSrcFrSwap. This is different from traditional power role swap
where the vbus sourcing is turned on by TCPM.

4. By this time, TCPC most likely would have started to
   source vbus, TCPM waits for tSrcFrSwap to see  if the
   lower level TCPC driver signals TCPM_SOURCING_VBUS event:
   FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED.
5. When TCPC signals sourcing vbus, TCPM sends PS_READY msg and
   changes the CC pin from Rd to Rp. This is the end of fast
   role swap sequence and TCPM initiates the sequnce to negotiate
   explicit contract by transitioning into SRC_STARTUP after
   SwapSrcStart.

The code is written based on the sequence described in "Figure 8-107:
Dual-role Port in Sink to Source Fast Role Swap State Diagram" of
USB Power Delivery Specification Revision 3.0, Version 1.2.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
- Rebased on top of usb-next and resolved conflicts due to the below
  changes:
  3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
  6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
- enable_frs sequence is now run as part of the same kthread that runs
  the state machines.
- Fixed the implicit fallthrough warning in the switch case for
  FR_SWAP_CANCEL case.
---
 drivers/usb/typec/tcpm/tcpm.c | 217 +++++++++++++++++++++++++++++++++-
 include/linux/usb/pd.h        |  19 +--
 include/linux/usb/tcpm.h      |   8 +-
 include/linux/usb/typec.h     |  13 ++
 4 files changed, 245 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 92806547f485..083e7af107b2 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -106,6 +106,13 @@
 	S(VCONN_SWAP_TURN_ON_VCONN),		\
 	S(VCONN_SWAP_TURN_OFF_VCONN),		\
 						\
+	S(FR_SWAP_SEND),			\
+	S(FR_SWAP_SEND_TIMEOUT),		\
+	S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF),			\
+	S(FR_SWAP_SNK_SRC_NEW_SINK_READY),		\
+	S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED),	\
+	S(FR_SWAP_CANCEL),			\
+						\
 	S(SNK_TRY),				\
 	S(SNK_TRY_WAIT),			\
 	S(SNK_TRY_WAIT_DEBOUNCE),               \
@@ -127,6 +134,9 @@
 	S(GET_PPS_STATUS_SEND),			\
 	S(GET_PPS_STATUS_SEND_TIMEOUT),		\
 						\
+	S(GET_SINK_CAP),			\
+	S(GET_SINK_CAP_TIMEOUT),		\
+						\
 	S(ERROR_RECOVERY),			\
 	S(PORT_RESET),				\
 	S(PORT_RESET_WAIT_OFF)
@@ -175,6 +185,8 @@ enum adev_actions {
 #define TCPM_CC_EVENT		BIT(0)
 #define TCPM_VBUS_EVENT		BIT(1)
 #define TCPM_RESET_EVENT	BIT(2)
+#define TCPM_FRS_EVENT		BIT(3)
+#define TCPM_SOURCING_VBUS	BIT(4)
 
 #define LOG_BUFFER_ENTRIES	1024
 #define LOG_BUFFER_ENTRY_SIZE	128
@@ -184,6 +196,8 @@ enum adev_actions {
 #define SVID_DISCOVERY_MAX	16
 #define ALTMODE_DISCOVERY_MAX	(SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
 
+#define GET_SINK_CAP_RETRY_MS	100
+
 struct pd_mode_data {
 	int svid_index;		/* current SVID index		*/
 	int nsvids;
@@ -261,6 +275,8 @@ struct tcpm_port {
 	struct kthread_work state_machine;
 	struct hrtimer vdm_state_machine_timer;
 	struct kthread_work vdm_state_machine;
+	struct hrtimer enable_frs_timer;
+	struct kthread_work enable_frs;
 	bool state_machine_running;
 
 	struct completion tx_complete;
@@ -335,6 +351,12 @@ struct tcpm_port {
 	/* port belongs to a self powered device */
 	bool self_powered;
 
+	/* FRS */
+	enum frs_typec_current frs_current;
+
+	/* Sink caps have been queried */
+	bool sink_cap_done;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *dentry;
 	struct mutex logbuffer_lock;	/* log buffer access lock */
@@ -940,6 +962,16 @@ static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
 	}
 }
 
+static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
+{
+	if (delay_ms) {
+		hrtimer_start(&port->enable_frs_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
+	} else {
+		hrtimer_cancel(&port->enable_frs_timer);
+		kthread_queue_work(port->wq, &port->enable_frs);
+	}
+}
+
 static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
 			   unsigned int delay_ms)
 {
@@ -1669,6 +1701,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
 	unsigned int cnt = pd_header_cnt_le(msg->header);
 	unsigned int rev = pd_header_rev_le(msg->header);
 	unsigned int i;
+	enum frs_typec_current frs_current;
+	bool frs_enable;
+	int ret;
 
 	switch (type) {
 	case PD_DATA_SOURCE_CAP:
@@ -1738,7 +1773,21 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
 		/* We don't do anything with this at the moment... */
 		for (i = 0; i < cnt; i++)
 			port->sink_caps[i] = le32_to_cpu(msg->payload[i]);
+
+		frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >>
+			PDO_FIXED_FRS_CURR_SHIFT;
+		frs_enable = frs_current && (frs_current <= port->typec_caps.frs_current);
+		tcpm_log(port,
+			 "Port partner FRS capable partner_frs_current:%u port_frs_current:%u enable:%c",
+			 frs_current, port->typec_caps.frs_current, frs_enable ? 'y' : 'n');
+		if (frs_enable) {
+			ret  = port->tcpc->enable_frs(port->tcpc, true);
+			tcpm_log(port, "Enable FRS %s, ret:%d\n", ret ? "fail" : "success", ret);
+		}
+
 		port->nr_sink_caps = cnt;
+		port->sink_cap_done = true;
+		tcpm_set_state(port, SNK_READY, 0);
 		break;
 	case PD_DATA_VENDOR_DEF:
 		tcpm_handle_vdm_request(port, msg->payload, cnt);
@@ -1833,6 +1882,9 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
 		case VCONN_SWAP_WAIT_FOR_VCONN:
 			tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0);
 			break;
+		case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+			tcpm_set_state(port, FR_SWAP_SNK_SRC_NEW_SINK_READY, 0);
+			break;
 		default:
 			break;
 		}
@@ -1872,6 +1924,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
 					     -EAGAIN : -EOPNOTSUPP);
 			tcpm_set_state(port, VCONN_SWAP_CANCEL, 0);
 			break;
+		case FR_SWAP_SEND:
+			tcpm_set_state(port, FR_SWAP_CANCEL, 0);
+			break;
+		case GET_SINK_CAP:
+			port->sink_cap_done = true;
+			tcpm_set_state(port, ready_state(port), 0);
+			break;
 		default:
 			break;
 		}
@@ -1906,6 +1965,9 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
 		case VCONN_SWAP_SEND:
 			tcpm_set_state(port, VCONN_SWAP_START, 0);
 			break;
+		case FR_SWAP_SEND:
+			tcpm_set_state(port, FR_SWAP_SNK_SRC_TRANSITION_TO_OFF, 0);
+			break;
 		default:
 			break;
 		}
@@ -2806,6 +2868,10 @@ static void tcpm_reset_port(struct tcpm_port *port)
 	port->try_src_count = 0;
 	port->try_snk_count = 0;
 	port->usb_type = POWER_SUPPLY_USB_TYPE_C;
+	port->nr_sink_caps = 0;
+	port->sink_cap_done = false;
+	if (port->tcpc->enable_frs)
+		port->tcpc->enable_frs(port->tcpc, false);
 
 	power_supply_changed(port->psy);
 }
@@ -3356,10 +3422,9 @@ static void run_state_machine(struct tcpm_port *port)
 		tcpm_swap_complete(port, 0);
 		tcpm_typec_connect(port);
 		tcpm_check_send_discover(port);
+		mod_enable_frs_delayed_work(port, 0);
 		tcpm_pps_complete(port, port->pps_status);
-
 		power_supply_changed(port->psy);
-
 		break;
 
 	/* Accessory states */
@@ -3383,9 +3448,13 @@ static void run_state_machine(struct tcpm_port *port)
 		tcpm_set_state(port, HARD_RESET_START, 0);
 		break;
 	case HARD_RESET_START:
+		port->sink_cap_done = false;
+		if (port->tcpc->enable_frs)
+			port->tcpc->enable_frs(port->tcpc, false);
 		port->hard_reset_count++;
 		port->tcpc->set_pd_rx(port->tcpc, false);
 		tcpm_unregister_altmodes(port);
+		port->nr_sink_caps = 0;
 		port->send_discover = true;
 		if (port->pwr_role == TYPEC_SOURCE)
 			tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
@@ -3517,6 +3586,35 @@ static void run_state_machine(struct tcpm_port *port)
 		tcpm_set_state(port, ready_state(port), 0);
 		break;
 
+	case FR_SWAP_SEND:
+		if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP)) {
+			tcpm_set_state(port, ERROR_RECOVERY, 0);
+			break;
+		}
+		tcpm_set_state_cond(port, FR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE);
+		break;
+	case FR_SWAP_SEND_TIMEOUT:
+		tcpm_set_state(port, ERROR_RECOVERY, 0);
+		break;
+	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+		tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF);
+		break;
+	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+		if (port->vbus_source)
+			tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
+		else
+			tcpm_set_state(port, ERROR_RECOVERY, PD_T_RECEIVER_RESPONSE);
+		break;
+	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+		tcpm_set_pwr_role(port, TYPEC_SOURCE);
+		if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) {
+			tcpm_set_state(port, ERROR_RECOVERY, 0);
+			break;
+		}
+		tcpm_set_cc(port, tcpm_rp_cc(port));
+		tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START);
+		break;
+
 	/* PR_Swap states */
 	case PR_SWAP_ACCEPT:
 		tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
@@ -3640,6 +3738,12 @@ static void run_state_machine(struct tcpm_port *port)
 		else
 			tcpm_set_state(port, SNK_READY, 0);
 		break;
+	case FR_SWAP_CANCEL:
+		if (port->pwr_role == TYPEC_SOURCE)
+			tcpm_set_state(port, SRC_READY, 0);
+		else
+			tcpm_set_state(port, SNK_READY, 0);
+		break;
 
 	case BIST_RX:
 		switch (BDO_MODE_MASK(port->bist_request)) {
@@ -3674,6 +3778,14 @@ static void run_state_machine(struct tcpm_port *port)
 	case GET_PPS_STATUS_SEND_TIMEOUT:
 		tcpm_set_state(port, ready_state(port), 0);
 		break;
+	case GET_SINK_CAP:
+		tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP);
+		tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE);
+		break;
+	case GET_SINK_CAP_TIMEOUT:
+		port->sink_cap_done = true;
+		tcpm_set_state(port, ready_state(port), 0);
+		break;
 	case ERROR_RECOVERY:
 		tcpm_swap_complete(port, -EPROTO);
 		tcpm_pps_complete(port, -EPROTO);
@@ -3889,6 +4001,13 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
 		 * Ignore it.
 		 */
 		break;
+	case FR_SWAP_SEND:
+	case FR_SWAP_SEND_TIMEOUT:
+	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+		/* Do nothing, CC change expected */
+		break;
 
 	case PORT_RESET:
 	case PORT_RESET_WAIT_OFF:
@@ -3959,6 +4078,9 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
 	case SRC_TRY_DEBOUNCE:
 		/* Do nothing, waiting for sink detection */
 		break;
+	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+		tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
+		break;
 
 	case PORT_RESET:
 	case PORT_RESET_WAIT_OFF:
@@ -4038,6 +4160,14 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
 		 */
 		break;
 
+	case FR_SWAP_SEND:
+	case FR_SWAP_SEND_TIMEOUT:
+	case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+	case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+	case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+		/* Do nothing, vbus drop expected */
+		break;
+
 	default:
 		if (port->pwr_role == TYPEC_SINK &&
 		    port->attached)
@@ -4092,6 +4222,25 @@ static void tcpm_pd_event_handler(struct kthread_work *work)
 			if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
 				_tcpm_cc_change(port, cc1, cc2);
 		}
+		if (events & TCPM_FRS_EVENT) {
+			if (port->state == SNK_READY)
+				tcpm_set_state(port, FR_SWAP_SEND, 0);
+			else
+				tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
+		}
+		if (events & TCPM_SOURCING_VBUS) {
+			tcpm_log(port, "sourcing vbus");
+			/*
+			 * In fast role swap case TCPC autonomously sources vbus. Set vbus_source
+			 * true as TCPM wouldn't have called tcpm_set_vbus.
+			 *
+			 * When vbus is sourced on the command on TCPM i.e. TCPM called
+			 * tcpm_set_vbus to source vbus, vbus_source would already be true.
+			 */
+			port->vbus_source = true;
+			_tcpm_pd_vbus_on(port);
+		}
+
 		spin_lock(&port->pd_event_lock);
 	}
 	spin_unlock(&port->pd_event_lock);
@@ -4125,6 +4274,50 @@ void tcpm_pd_hard_reset(struct tcpm_port *port)
 }
 EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
 
+void tcpm_sink_frs(struct tcpm_port *port)
+{
+	spin_lock(&port->pd_event_lock);
+	port->pd_events = TCPM_FRS_EVENT;
+	spin_unlock(&port->pd_event_lock);
+	kthread_queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_sink_frs);
+
+void tcpm_sourcing_vbus(struct tcpm_port *port)
+{
+	spin_lock(&port->pd_event_lock);
+	port->pd_events = TCPM_SOURCING_VBUS;
+	spin_unlock(&port->pd_event_lock);
+	kthread_queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_sourcing_vbus);
+
+static void tcpm_enable_frs_work(struct kthread_work *work)
+{
+	struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs);
+
+	mutex_lock(&port->lock);
+	/* Not FRS capable */
+	if (!port->connected || port->port_type != TYPEC_PORT_DRP ||
+	    port->pwr_opmode != TYPEC_PWR_MODE_PD ||
+	    !port->tcpc->enable_frs ||
+	    /* Sink caps queried */
+	    port->sink_cap_done || port->negotiated_rev < PD_REV30)
+		goto unlock;
+
+	/* Send when the state machine is idle */
+	if (port->state != SNK_READY || port->vdm_state != VDM_STATE_DONE || port->send_discover)
+		goto resched;
+
+	tcpm_set_state(port, GET_SINK_CAP, 0);
+	port->sink_cap_done = true;
+
+resched:
+	mod_enable_frs_delayed_work(port, GET_SINK_CAP_RETRY_MS);
+unlock:
+	mutex_unlock(&port->lock);
+}
+
 static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data)
 {
 	struct tcpm_port *port = typec_get_drvdata(p);
@@ -4532,7 +4725,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
 {
 	const char *cap_str;
 	int ret;
-	u32 mw;
+	u32 mw, frs_current;
 
 	if (!fwnode)
 		return -EINVAL;
@@ -4601,6 +4794,13 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
 
 	port->self_powered = fwnode_property_read_bool(fwnode, "self-powered");
 
+	/* FRS can only be supported byb DRP ports */
+	if (port->port_type == TYPEC_PORT_DRP) {
+		ret = fwnode_property_read_u32(fwnode, "frs-typec-current", &frs_current);
+		if (ret >= 0 && frs_current <= FRS_5V_3A)
+			port->typec_caps.frs_current = frs_current;
+	}
+
 	return 0;
 }
 
@@ -4845,6 +5045,14 @@ static enum hrtimer_restart vdm_state_machine_timer_handler(struct hrtimer *time
 	return HRTIMER_NORESTART;
 }
 
+static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer)
+{
+	struct tcpm_port *port = container_of(timer, struct tcpm_port, enable_frs_timer);
+
+	kthread_queue_work(port->wq, &port->enable_frs);
+	return HRTIMER_NORESTART;
+}
+
 struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 {
 	struct tcpm_port *port;
@@ -4874,10 +5082,13 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
 	kthread_init_work(&port->state_machine, tcpm_state_machine_work);
 	kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
 	kthread_init_work(&port->event_work, tcpm_pd_event_handler);
+	kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
 	hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	port->state_machine_timer.function = state_machine_timer_handler;
 	hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
+	hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	port->enable_frs_timer.function = enable_frs_timer_handler;
 
 	spin_lock_init(&port->pd_event_lock);
 
diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h
index f842e4589bd2..3a805e2ecbc9 100644
--- a/include/linux/usb/pd.h
+++ b/include/linux/usb/pd.h
@@ -219,14 +219,16 @@ enum pd_pdo_type {
 #define PDO_CURR_MASK		0x3ff
 #define PDO_PWR_MASK		0x3ff
 
-#define PDO_FIXED_DUAL_ROLE	BIT(29)	/* Power role swap supported */
-#define PDO_FIXED_SUSPEND	BIT(28) /* USB Suspend supported (Source) */
-#define PDO_FIXED_HIGHER_CAP	BIT(28) /* Requires more than vSafe5V (Sink) */
-#define PDO_FIXED_EXTPOWER	BIT(27) /* Externally powered */
-#define PDO_FIXED_USB_COMM	BIT(26) /* USB communications capable */
-#define PDO_FIXED_DATA_SWAP	BIT(25) /* Data role swap supported */
-#define PDO_FIXED_VOLT_SHIFT	10	/* 50mV units */
-#define PDO_FIXED_CURR_SHIFT	0	/* 10mA units */
+#define PDO_FIXED_DUAL_ROLE		BIT(29)	/* Power role swap supported */
+#define PDO_FIXED_SUSPEND		BIT(28) /* USB Suspend supported (Source) */
+#define PDO_FIXED_HIGHER_CAP		BIT(28) /* Requires more than vSafe5V (Sink) */
+#define PDO_FIXED_EXTPOWER		BIT(27) /* Externally powered */
+#define PDO_FIXED_USB_COMM		BIT(26) /* USB communications capable */
+#define PDO_FIXED_DATA_SWAP		BIT(25) /* Data role swap supported */
+#define PDO_FIXED_FRS_CURR_MASK		(BIT(24) | BIT(23)) /* FR_Swap Current (Sink) */
+#define PDO_FIXED_FRS_CURR_SHIFT	23
+#define PDO_FIXED_VOLT_SHIFT		10	/* 50mV units */
+#define PDO_FIXED_CURR_SHIFT		0	/* 10mA units */
 
 #define PDO_FIXED_VOLT(mv)	((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
 #define PDO_FIXED_CURR(ma)	((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
@@ -454,6 +456,7 @@ static inline unsigned int rdo_max_power(u32 rdo)
 #define PD_T_DB_DETECT		10000	/* 10 - 15 seconds */
 #define PD_T_SEND_SOURCE_CAP	150	/* 100 - 200 ms */
 #define PD_T_SENDER_RESPONSE	60	/* 24 - 30 ms, relaxed */
+#define PD_T_RECEIVER_RESPONSE	15	/* 15ms max */
 #define PD_T_SOURCE_ACTIVITY	45
 #define PD_T_SINK_ACTIVITY	135
 #define PD_T_SINK_WAIT_CAP	240
diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
index 89f58760cf48..09762d26fa0c 100644
--- a/include/linux/usb/tcpm.h
+++ b/include/linux/usb/tcpm.h
@@ -78,8 +78,11 @@ enum tcpm_transmit_type {
  *		automatically if a connection is established.
  * @try_role:	Optional; called to set a preferred role
  * @pd_transmit:Called to transmit PD message
- * @mux:	Pointer to multiplexer data
  * @set_bist_data: Turn on/off bist data mode for compliance testing
+ * @enable_frs:
+ *		Optional; Called to enable/disable PD 3.0 fast role swap.
+ *		Enabling frs is accessory dependent as not all PD3.0
+ *		accessories support fast role swap.
  */
 struct tcpc_dev {
 	struct fwnode_handle *fwnode;
@@ -105,6 +108,7 @@ struct tcpc_dev {
 	int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
 			   const struct pd_message *msg);
 	int (*set_bist_data)(struct tcpc_dev *dev, bool on);
+	int (*enable_frs)(struct tcpc_dev *dev, bool enable);
 };
 
 struct tcpm_port;
@@ -114,6 +118,8 @@ void tcpm_unregister_port(struct tcpm_port *port);
 
 void tcpm_vbus_change(struct tcpm_port *port);
 void tcpm_cc_change(struct tcpm_port *port);
+void tcpm_sink_frs(struct tcpm_port *port);
+void tcpm_sourcing_vbus(struct tcpm_port *port);
 void tcpm_pd_receive(struct tcpm_port *port,
 		     const struct pd_message *msg);
 void tcpm_pd_transmit_complete(struct tcpm_port *port,
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 9cb1bec94b71..2ec7451a14ab 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -204,6 +204,18 @@ struct typec_operations {
 			     enum typec_port_type type);
 };
 
+/*
+ * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
+ * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
+ * Version 1.2"
+ */
+enum frs_typec_current {
+	FRS_NOT_SUPPORTED,
+	FRS_DEFAULT_POWER,
+	FRS_5V_1P5A,
+	FRS_5V_3A,
+};
+
 /*
  * struct typec_capability - USB Type-C Port Capabilities
  * @type: Supported power role of the port
@@ -226,6 +238,7 @@ struct typec_capability {
 	int			prefer_role;
 	enum typec_accessory	accessory[TYPEC_MAX_ACCESSORY];
 	unsigned int		orientation_aware:1;
+	enum frs_typec_current	frs_current;
 
 	struct fwnode_handle	*fwnode;
 	void			*driver_data;
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (8 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS) Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 13:23   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS Badhri Jagan Sridharan
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Implement tcpc.enable_frs to enable TCPC to receive
Fast role swap signal.

Additionally set the sink disconnect threshold to 4v
to prevent disconnect during Fast Role swap.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 17 +++++++++++++++++
 drivers/usb/typec/tcpm/tcpci.h |  8 ++++++++
 2 files changed, 25 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index d6a6fac82d48..f9f0af64da5f 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -268,6 +268,22 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
 				enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
 }
 
+static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(dev);
+	int ret;
+
+	/* To prevent disconnect during FRS, set disconnect threshold to 3.5V */
+	ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ?
+				 TCPC_FAST_ROLE_SWAP_EN : 0);
+
+	return ret;
+}
+
 static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
 {
 	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -611,6 +627,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
 	tcpci->tcpc.set_roles = tcpci_set_roles;
 	tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
 	tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
+	tcpci->tcpc.enable_frs = tcpci_enable_frs;
 
 	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 82f021a82456..5ef07a56d67a 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -16,6 +16,7 @@
 #define TCPC_PD_INT_REV			0xa
 
 #define TCPC_ALERT			0x10
+#define TCPC_ALERT_EXTND		BIT(14)
 #define TCPC_ALERT_EXTENDED_STATUS	BIT(13)
 #define TCPC_ALERT_VBUS_DISCNCT		BIT(11)
 #define TCPC_ALERT_RX_BUF_OVF		BIT(10)
@@ -37,6 +38,9 @@
 #define TCPC_EXTENDED_STATUS_MASK		0x16
 #define TCPC_EXTENDED_STATUS_MASK_VSAFE0V	BIT(0)
 
+#define TCPC_ALERT_EXTENDED_MASK	0x17
+#define TCPC_SINK_FAST_ROLE_SWAP	BIT(0)
+
 #define TCPC_CONFIG_STD_OUTPUT		0x18
 
 #define TCPC_TCPC_CTRL			0x19
@@ -63,6 +67,7 @@
 
 #define TCPC_POWER_CTRL			0x1c
 #define TCPC_POWER_CTRL_VCONN_ENABLE	BIT(0)
+#define TCPC_FAST_ROLE_SWAP_EN		BIT(7)
 
 #define TCPC_CC_STATUS			0x1d
 #define TCPC_CC_STATUS_TOGGLING		BIT(5)
@@ -74,11 +79,14 @@
 
 #define TCPC_POWER_STATUS		0x1e
 #define TCPC_POWER_STATUS_UNINIT	BIT(6)
+#define TCPC_POWER_STATUS_SOURCING_VBUS	BIT(4)
 #define TCPC_POWER_STATUS_VBUS_DET	BIT(3)
 #define TCPC_POWER_STATUS_VBUS_PRES	BIT(2)
 
 #define TCPC_FAULT_STATUS		0x1f
 
+#define TCPC_ALERT_EXTENDED		0x21
+
 #define TCPC_COMMAND			0x23
 #define TCPC_CMD_WAKE_I2C		0x11
 #define TCPC_CMD_DISABLE_VBUS_DETECT	0x22
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (9 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 13:25   ` Heikki Krogerus
  2020-09-01  2:59 ` [PATCH v6 12/14] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support Badhri Jagan Sridharan
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

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 <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 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 b61f290a8f96..6ba808ad901a 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.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 12/14] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (10 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 13/14] usb: typec: tcpci: Implement Auto discharge disconnect callbacks Badhri Jagan Sridharan
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

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 <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpm.c | 74 +++++++++++++++++++++++++++++++++--
 include/linux/usb/tcpm.h      | 16 ++++++++
 2 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 083e7af107b2..7aca769727d8 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1694,6 +1694,25 @@ 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_role port_role,
+						  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, port_role, mode, pps_active,
+							    requested_vbus_voltage);
+	tcpm_log_force(port,
+		       "set_auto_vbus_discharge_threshold pwr_role:%s mode:%d pps_active:%c vbus:%u ret:%d",
+		       port_role == TYPEC_SINK ? "sink" : "source", 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)
 {
@@ -1863,6 +1882,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_SINK,
+								       TYPEC_PWR_MODE_PD,
+								       port->pps_data.active,
+								       port->supply_voltage);
 				tcpm_set_state(port, SNK_READY, 0);
 			} else {
 				/*
@@ -2777,8 +2800,14 @@ 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) {
+		tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SOURCE, 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_SOURCE, tcpm_data_role_for_sink(port));
 	if (ret < 0)
 		return ret;
 
@@ -2845,6 +2874,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;
@@ -2909,8 +2944,14 @@ 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_SINK, 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;
 
@@ -3470,6 +3511,8 @@ static void run_state_machine(struct tcpm_port *port)
 		 * drive VBUS to vSafe0V as shown in Figure 7-9.
 		 */
 		tcpm_set_vconn(port, false);
+		tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SOURCE, TYPEC_PWR_MODE_USB,
+						       false, 0);
 		tcpm_set_vbus(port, false);
 		tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
 			       tcpm_data_role_for_source(port));
@@ -3491,9 +3534,13 @@ static void run_state_machine(struct tcpm_port *port)
 		tcpm_set_vbus(port, true);
 		port->tcpc->set_pd_rx(port->tcpc, true);
 		tcpm_set_attached_state(port, true);
+		tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SOURCE, TYPEC_PWR_MODE_USB,
+						       false, VSAFE5V);
 		tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
 		break;
 	case SNK_HARD_RESET_SINK_OFF:
+		tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SINK, TYPEC_PWR_MODE_USB, false,
+						       0);
 		memset(&port->pps_data, 0, sizeof(port->pps_data));
 		tcpm_set_vconn(port, false);
 		if (port->pd_capable)
@@ -3536,6 +3583,8 @@ 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_SINK, TYPEC_PWR_MODE_USB, false,
+						       VSAFE5V);
 		tcpm_set_state(port, SNK_STARTUP, 0);
 		break;
 
@@ -3637,6 +3686,12 @@ 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_auto_vbus_discharge_threshold(port, TYPEC_SOURCE, TYPEC_PWR_MODE_USB,
+						       port->pps_data.active, 0);
 		tcpm_set_vbus(port, false);
 		port->explicit_contract = false;
 		/* allow time for Vbus discharge, must be < tSrcSwapStdby */
@@ -3665,9 +3720,18 @@ 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_SINK, 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_SINK, 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);
@@ -3693,6 +3757,8 @@ static void run_state_machine(struct tcpm_port *port)
 		 */
 		tcpm_set_pwr_role(port, TYPEC_SOURCE);
 		tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
+		tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_SOURCE, TYPEC_PWR_MODE_USB,
+						       false, VSAFE5V);
 		tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START);
 		break;
 
diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
index 09762d26fa0c..a1707def51c3 100644
--- a/include/linux/usb/tcpm.h
+++ b/include/linux/usb/tcpm.h
@@ -83,6 +83,18 @@ enum tcpm_transmit_type {
  *		Optional; Called to enable/disable PD 3.0 fast role swap.
  *		Enabling frs is accessory dependent as not all PD3.0
  *		accessories support fast role swap.
+ * @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;
@@ -109,6 +121,10 @@ struct tcpc_dev {
 			   const struct pd_message *msg);
 	int (*set_bist_data)(struct tcpc_dev *dev, bool on);
 	int (*enable_frs)(struct tcpc_dev *dev, bool enable);
+	int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable);
+	int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_role port_role,
+						 enum typec_pwr_opmode mode, bool pps_active,
+						 u32 requested_vbus_voltage);
 };
 
 struct tcpm_port;
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 13/14] usb: typec: tcpci: Implement Auto discharge disconnect callbacks
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (11 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 12/14] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-01  2:59 ` [PATCH v6 14/14] usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold Badhri Jagan Sridharan
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Implement callbacks for enabling/disabling
POWER_CONTROL.AutoDischargeDisconnect.

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 <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci.c | 33 +++++++++++++++++++++++++++++++++
 drivers/usb/typec/tcpm/tcpci.h | 22 ++++++++++++++++++++--
 2 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index f9f0af64da5f..4ca64e8c8fe8 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -268,6 +268,33 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
 				enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
 }
 
+static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(dev);
+	int ret;
+
+	ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_POWER_CTRL_AUTO_DISCHARGE,
+				 enable ? TCPC_POWER_CTRL_AUTO_DISCHARGE : 0);
+	return ret;
+}
+
+static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_role port_role,
+						   enum typec_pwr_opmode mode, bool pps_active,
+						   u32 requested_vbus_voltage)
+{
+	struct tcpci *tcpci = tcpc_to_tcpci(dev);
+	int (*set_auto_vbus_threshold)(struct tcpci *tcpci, struct tcpci_data *data,
+				       enum typec_role port_role, enum typec_pwr_opmode mode,
+				       bool pps_active, u32 requested_vbus_voltage);
+
+	set_auto_vbus_threshold = tcpci->data->set_auto_vbus_discharge_threshold;
+	if (set_auto_vbus_threshold)
+		return set_auto_vbus_threshold(tcpci, tcpci->data, port_role, mode, pps_active,
+					       requested_vbus_voltage);
+
+	return 0;
+}
+
 static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
 {
 	struct tcpci *tcpci = tcpc_to_tcpci(dev);
@@ -629,6 +656,12 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
 	tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
 	tcpci->tcpc.enable_frs = tcpci_enable_frs;
 
+	if (tcpci->data->auto_discharge_disconnect) {
+		tcpci->tcpc.enable_auto_vbus_discharge = tcpci_enable_auto_vbus_discharge;
+		tcpci->tcpc.set_auto_vbus_discharge_threshold =
+			tcpci_set_auto_vbus_discharge_threshold;
+	}
+
 	err = tcpci_parse_config(tcpci);
 	if (err < 0)
 		return ERR_PTR(err);
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 5ef07a56d67a..6a0aea34e544 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -8,6 +8,8 @@
 #ifndef __LINUX_USB_TCPCI_H
 #define __LINUX_USB_TCPCI_H
 
+#include <linux/usb/typec.h>
+
 #define TCPC_VENDOR_ID			0x0
 #define TCPC_PRODUCT_ID			0x2
 #define TCPC_BCD_DEV			0x4
@@ -67,6 +69,7 @@
 
 #define TCPC_POWER_CTRL			0x1c
 #define TCPC_POWER_CTRL_VCONN_ENABLE	BIT(0)
+#define TCPC_POWER_CTRL_AUTO_DISCHARGE	BIT(4)
 #define TCPC_FAST_ROLE_SWAP_EN		BIT(7)
 
 #define TCPC_CC_STATUS			0x1d
@@ -133,6 +136,8 @@
 
 #define TCPC_VBUS_VOLTAGE			0x70
 #define TCPC_VBUS_SINK_DISCONNECT_THRESH	0x72
+#define TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB	25
+#define TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX	1023
 #define TCPC_VBUS_STOP_DISCHARGE_THRESH		0x74
 #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG		0x76
 #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG		0x78
@@ -140,20 +145,33 @@
 /* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */
 #define TCPC_TRANSMIT_BUFFER_MAX_LEN		31
 
+struct tcpci;
+
 /*
- * @TX_BUF_BYTE_x_hidden
+ * @TX_BUF_BYTE_x_hidden:
  *		optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
+ * @auto_discharge_disconnect:
+ *		Optional; Enables TCPC to autonously discharge vbus on disconnect.
+ * @set_auto_vbus_discharge_threshold:
+ *		Mandatory when @auto_discharge_disconnect is sets. Allows
+ *		programming the voltage threshold of vbus below which TCPC
+ *		enables the vbus discharge circuit.
  */
-struct tcpci;
 struct tcpci_data {
 	struct regmap *regmap;
 	unsigned char TX_BUF_BYTE_x_hidden:1;
+	unsigned char auto_discharge_disconnect:1;
+
 	int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
 	int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
 			 bool enable);
 	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);
+	int (*set_auto_vbus_discharge_threshold)(struct tcpci *tcpci, struct tcpci_data *data,
+						 enum typec_role port_role,
+						 enum typec_pwr_opmode mode, bool pps_active,
+						 u32 requested_vbus_voltage);
 };
 
 struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v6 14/14] usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (12 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 13/14] usb: typec: tcpci: Implement Auto discharge disconnect callbacks Badhri Jagan Sridharan
@ 2020-09-01  2:59 ` Badhri Jagan Sridharan
  2020-09-15 12:09 ` [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Heikki Krogerus
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-01  2:59 UTC (permalink / raw)
  To: Guenter Roeck, Heikki Krogerus, Greg Kroah-Hartman
  Cc: linux-usb, linux-kernel, Badhri Jagan Sridharan

Programs VBUS_SINK_DISCONNECT_THRESHOLD based on the power_role,
voltage requested as sink, mode of operation.

The programmed threshold is based on vSinkDisconnect and
vSinkDisconnectPD values.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
Changes since v1:
- Changing patch version to v6 to fix version number confusion.
---
 drivers/usb/typec/tcpm/tcpci_maxim.c | 48 ++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
index 6ba808ad901a..e35bd995c037 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -137,6 +137,52 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
 		return;
 }
 
+static int max_tcpci_set_auto_vbus_discharge_threshold(struct tcpci *tcpci, struct tcpci_data *data,
+						       enum typec_role port_role,
+						       enum typec_pwr_opmode mode, bool pps_active,
+						       u32 requested_vbus_voltage_mv)
+{
+	struct max_tcpci_chip *chip = tdata_to_max_tcpci(data);
+	u32 threshold = 0;
+	u8 pwr_ctrl;
+
+	/*
+	 * Indicates that vbus is going to go away due PR_SWAP, hard reset etc.
+	 * Do not discharge vbus here.
+	 */
+	if (requested_vbus_voltage_mv == 0)
+		goto write_thresh;
+
+	if (port_role == TYPEC_SINK) {
+		max_tcpci_read8(chip, TCPC_POWER_CTRL, &pwr_ctrl);
+		if (pwr_ctrl & TCPC_FAST_ROLE_SWAP_EN) {
+			/* To prevent disconnect when the source is fast role swap is capable. */
+			threshold = 3500;
+		} else if (mode == TYPEC_PWR_MODE_PD) {
+			if (pps_active)
+				threshold = (95 * requested_vbus_voltage_mv / 100) - 850;
+			else
+				threshold = (95 * requested_vbus_voltage_mv / 100) - 1250;
+		} else {
+			/* 3.5V for non-pd sink */
+			threshold = 3500;
+		}
+	} else {
+		/* 4V for source */
+		threshold = 4000;
+	}
+
+	threshold = threshold / TCPC_VBUS_SINK_DISCONNECT_THRESH_LSB;
+
+	if (threshold > TCPC_VBUS_SINK_DISCONNECT_THRESH_MAX) {
+		dev_err(chip->dev, "VBUS_SINK_DISCONNECT_THRESH out of range");
+		return -EINVAL;
+	}
+
+write_thresh:
+	return max_tcpci_write16(chip, TCPC_VBUS_SINK_DISCONNECT_THRESH, threshold);
+}
+
 static void process_rx(struct max_tcpci_chip *chip, u16 status)
 {
 	struct pd_message msg;
@@ -454,6 +500,8 @@ 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.set_auto_vbus_discharge_threshold = max_tcpci_set_auto_vbus_discharge_threshold;
+	chip->data.auto_discharge_disconnect = true;
 
 	max_tcpci_init_regs(chip);
 	chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
-- 
2.28.0.402.g5ffc5be6b7-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (13 preceding siblings ...)
  2020-09-01  2:59 ` [PATCH v6 14/14] usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold Badhri Jagan Sridharan
@ 2020-09-15 12:09 ` Heikki Krogerus
  2020-09-15 16:57   ` Badhri Jagan Sridharan
  2020-09-15 13:33 ` Heikki Krogerus
  2020-09-16 11:05 ` Greg Kroah-Hartman
  16 siblings, 1 reply; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:09 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:13PM -0700, Badhri Jagan Sridharan wrote:
> First of all apologies for mixing up the patch version as noted by
> Heikki and Greg. All of them were v1's but since I was manually adding
> the version numbers I mixed them up. Using the --reroll-count option
> now. Updating the patch version to v6 (highest version number in the
> previous patchset + 1) to avoid confusion.

If this is v6, then where are v2 - v5? And what changed? Why didn't
you just make this v2?


> I also rebased on to off of the recent usb-next tip:
> 5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
> Which had the following changes causing merge conflict:
> 3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> 6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> 
> Addressed comments from Heikki and Randy which have described in the
> individual commit's change history as well.
> 
> Badhri Jagan Sridharan (14):
>   usb: typec: tcpci: Add register definitions to tcpci
>   usb: typec: tcpci: Add support when hidden tx registers are
>     inaccessible
>   usb: typec: tcpci: update ROLE_CONTROL for DRP
>   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: Implement enabling Auto Discharge disconnect support
>   usb: typec: tcpci: Implement Auto discharge disconnect callbacks
>   usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold
> 
>  .../bindings/connector/usb-connector.txt      | 128 ++++
>  .../devicetree/bindings/usb/maxim,tcpci.txt   |  44 ++
>  drivers/usb/typec/tcpm/Kconfig                |   5 +
>  drivers/usb/typec/tcpm/Makefile               |  13 +-
>  drivers/usb/typec/tcpm/tcpci.c                | 146 ++++-
>  drivers/usb/typec/tcpm/tcpci.h                |  43 ++
>  drivers/usb/typec/tcpm/tcpci_maxim.c          | 564 ++++++++++++++++++
>  drivers/usb/typec/tcpm/tcpm.c                 | 291 ++++++++-
>  include/dt-bindings/usb/pd.h                  |  10 +
>  include/linux/usb/pd.h                        |  19 +-
>  include/linux/usb/tcpm.h                      |  24 +-
>  include/linux/usb/typec.h                     |  13 +
>  12 files changed, 1266 insertions(+), 34 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/connector/usb-connector.txt
>  create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.txt
>  create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c
> 
> 
> base-commit: 5fedf0d295d3ef69fd85fdee4cb68fd3756b54c2
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci
  2020-09-01  2:59 ` [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci Badhri Jagan Sridharan
@ 2020-09-15 12:10   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:10 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:14PM -0700, Badhri Jagan Sridharan wrote:
> Add register definitions to trap extended alerts.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Change history:
> - No code changes. Update version from v1 to v6 to avoid confusion
> ---
>  drivers/usb/typec/tcpm/tcpci.h | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index 11c36d086c86..fd26ca35814c 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -16,6 +16,7 @@
>  #define TCPC_PD_INT_REV			0xa
>  
>  #define TCPC_ALERT			0x10
> +#define TCPC_ALERT_EXTENDED_STATUS	BIT(13)
>  #define TCPC_ALERT_VBUS_DISCNCT		BIT(11)
>  #define TCPC_ALERT_RX_BUF_OVF		BIT(10)
>  #define TCPC_ALERT_FAULT		BIT(9)
> @@ -32,6 +33,10 @@
>  #define TCPC_ALERT_MASK			0x12
>  #define TCPC_POWER_STATUS_MASK		0x14
>  #define TCPC_FAULT_STATUS_MASK		0x15
> +
> +#define TCPC_EXTENDED_STATUS_MASK		0x16
> +#define TCPC_EXTENDED_STATUS_MASK_VSAFE0V	BIT(0)
> +
>  #define TCPC_CONFIG_STD_OUTPUT		0x18
>  
>  #define TCPC_TCPC_CTRL			0x19
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible
  2020-09-01  2:59 ` [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible Badhri Jagan Sridharan
@ 2020-09-15 12:11   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:11 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:15PM -0700, Badhri Jagan Sridharan wrote:
> TCPCI spec forbids direct access of TX_BUF_BYTE_x register.
> The existing version of tcpci driver assumes that those registers
> are directly addressible. Add support for tcpci chips which do
> not support direct access to TX_BUF_BYTE_x registers. TX_BUF_BYTE_x
> can only be accessed by I2C_WRITE_BYTE_COUNT.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Changes since v1:
>  - Refactored the code to check for TX_BUF_BYTE_x_hidden as suggested by
>    Heikki.
>  - Not formatting the tcpci_pd_transmit to follow the 100 character/line
>    limit as suggested by Heikki. (Should be a separate change).
>  - Changing patch version to v6 to fix version number confusion.
> ---
>  drivers/usb/typec/tcpm/tcpci.c | 46 ++++++++++++++++++++++++++--------
>  drivers/usb/typec/tcpm/tcpci.h |  8 ++++++
>  2 files changed, 43 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index bd80e03b2b6f..7d36d5e2d3f7 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -330,23 +330,47 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
>  	int ret;
>  
>  	cnt = msg ? pd_header_cnt(header) * 4 : 0;
> -	ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
> -	if (ret < 0)
> -		return ret;
> +	/**
> +	 * TCPCI spec forbids direct access of TCPC_TX_DATA.
> +	 * But, since some of the chipsets offer this capability,
> +	 * it's fair to support both.
> +	 */
> +	if (tcpci->data->TX_BUF_BYTE_x_hidden) {
> +		u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,};
> +		u8 pos = 0;
>  
> -	ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
> -	if (ret < 0)
> -		return ret;
> +		/* Payload + header + TCPC_TX_BYTE_CNT */
> +		buf[pos++] = cnt + 2;
> +
> +		if (msg)
> +			memcpy(&buf[pos], &msg->header, sizeof(msg->header));
> +
> +		pos += sizeof(header);
>  
> -	if (cnt > 0) {
> -		ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
> -				       &msg->payload, cnt);
> +		if (cnt > 0)
> +			memcpy(&buf[pos], msg->payload, cnt);
> +
> +		pos += cnt;
> +		ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
>  		if (ret < 0)
>  			return ret;
> +
> +		ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (cnt > 0) {
> +			ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt);
> +			if (ret < 0)
> +				return ret;
> +		}
>  	}
>  
> -	reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
> -		(type << TCPC_TRANSMIT_TYPE_SHIFT);
> +	reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
>  	ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
>  	if (ret < 0)
>  		return ret;
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index fd26ca35814c..cf9d8b63adcb 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -128,9 +128,17 @@
>  #define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG		0x76
>  #define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG		0x78
>  
> +/* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */
> +#define TCPC_TRANSMIT_BUFFER_MAX_LEN		31
> +
> +/*
> + * @TX_BUF_BYTE_x_hidden
> + *		optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
> + */
>  struct tcpci;
>  struct tcpci_data {
>  	struct regmap *regmap;
> +	unsigned char TX_BUF_BYTE_x_hidden:1;
>  	int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
>  	int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
>  			 bool enable);
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP
  2020-09-01  2:59 ` [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP Badhri Jagan Sridharan
@ 2020-09-15 12:12   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:12 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:16PM -0700, Badhri Jagan Sridharan wrote:
> ROLE_CONTROL register would not have the actual CC terminations
> unless the port does not set ROLE_CONTROL.DRP. For DRP ports,
> CC_STATUS.cc1/cc2 indicates the final terminations applied
> when TCPC enters potential_connect_as_source/_sink.
> For DRP ports, infer port role from CC_STATUS and set corresponding
> CC terminations before setting the orientation.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Changes since v1:
>  - Changing patch version to v6 to fix version number confusion.
> ---
>  drivers/usb/typec/tcpm/tcpci.c | 37 +++++++++++++++++++++++++++++++++-
>  1 file changed, 36 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index 7d36d5e2d3f7..7d9491ba62fb 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -191,12 +191,47 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
>  	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
>  	unsigned int reg;
>  	int ret;
> +	enum typec_cc_status cc1, cc2;
>  
> -	/* Keep the disconnect cc line open */
> +	/* Obtain Rp setting from role control */
>  	ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &reg);
>  	if (ret < 0)
>  		return ret;
>  
> +	ret = tcpci_get_cc(tcpc, &cc1, &cc2);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * When port has drp toggling enabled, ROLE_CONTROL would only have the initial
> +	 * terminations for the toggling and does not indicate the final cc
> +	 * terminations when ConnectionResult is 0 i.e. drp toggling stops and
> +	 * the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the
> +	 * terminations seen. The port role is then used to set the cc terminations.
> +	 */
> +	if (reg & TCPC_ROLE_CTRL_DRP) {
> +		/* Disable DRP for the OPEN setting to take effect */
> +		reg = reg & ~TCPC_ROLE_CTRL_DRP;
> +
> +		if (polarity == TYPEC_POLARITY_CC2) {
> +			reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
> +			/* Local port is source */
> +			if (cc2 == TYPEC_CC_RD)
> +				/* Role control would have the Rp setting when DRP was enabled */
> +				reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
> +			else
> +				reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
> +		} else {
> +			reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
> +			/* Local port is source */
> +			if (cc1 == TYPEC_CC_RD)
> +				/* Role control would have the Rp setting when DRP was enabled */
> +				reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
> +			else
> +				reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
> +		}
> +	}
> +
>  	if (polarity == TYPEC_POLARITY_CC2)
>  		reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
>  	else
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference
  2020-09-01  2:59 ` [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference Badhri Jagan Sridharan
@ 2020-09-15 12:16   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:16 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:17PM -0700, Badhri Jagan Sridharan wrote:
> Allow chip level drivers to retrieve reference to tcpm_port.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>

I'm still wondering if this is the right way. Ideally the glue drivers
should not need to deal with the tcpm_port at all directly, only with
the tcpci handle they have. But FWIW:

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Change since v1:
> - Changing patch version to v6 to fix version number confusion.
> ---
>  drivers/usb/typec/tcpm/tcpci.c | 6 ++++++
>  drivers/usb/typec/tcpm/tcpci.h | 2 ++
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index 7d9491ba62fb..b960fe5a0f28 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -38,6 +38,12 @@ struct tcpci_chip {
>  	struct tcpci_data data;
>  };
>  
> +struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci)
> +{
> +	return tcpci->port;
> +}
> +EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port);
> +
>  static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
>  {
>  	return container_of(tcpc, struct tcpci, tcpc);
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index cf9d8b63adcb..04c49a0b0368 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -150,4 +150,6 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
>  void tcpci_unregister_port(struct tcpci *tcpci);
>  irqreturn_t tcpci_irq(struct tcpci *tcpci);
>  
> +struct tcpm_port;
> +struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci);
>  #endif /* __LINUX_USB_TCPCI_H */
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback
  2020-09-01  2:59 ` [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback Badhri Jagan Sridharan
@ 2020-09-15 12:22   ` Heikki Krogerus
  2020-09-16  9:07     ` Badhri Jagan Sridharan
  0 siblings, 1 reply; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:22 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:18PM -0700, Badhri Jagan Sridharan wrote:
> set_vbus callback allows TCPC which are TCPCI based, however,
> does not support turning on sink and source mode through
> Command.SinkVbus and Command.SourceVbusDefaultVoltage.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> ---
>  drivers/usb/typec/tcpm/tcpci.c | 7 +++++++
>  drivers/usb/typec/tcpm/tcpci.h | 1 +
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index b960fe5a0f28..d6a6fac82d48 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -328,6 +328,13 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
>  	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
>  	int ret;
>  
> +	if (tcpci->data->set_vbus) {
> +		ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink);
> +		/* Bypass when ret > 0 */
> +		if (ret != 0)
> +			return ret < 0 ? ret : 0;

Can it return positive value? What does positive value mean?

> +	}
> +
>  	/* Disable both source and sink first before enabling anything */
>  
>  	if (!source) {
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index 04c49a0b0368..4d441bdf24d5 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -144,6 +144,7 @@ struct tcpci_data {
>  			 bool enable);
>  	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);
>  };
>  
>  struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver
  2020-09-01  2:59 ` [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver Badhri Jagan Sridharan
@ 2020-09-15 12:43   ` Heikki Krogerus
  2020-09-16  9:34     ` Badhri Jagan Sridharan
  0 siblings, 1 reply; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 12:43 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:20PM -0700, Badhri Jagan Sridharan wrote:
> Chip level TCPC driver for Maxim's TCPCI implementation.
> This TCPC implementation does not support the following
> commands: COMMAND.SinkVbus, COMMAND.SourceVbusDefaultVoltage,
> COMMAND.SourceVbusHighVoltage. Instead the sinking and sourcing
> from vbus is supported by writes to custom registers.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> - Removed setting USB_PSY and terminating description with period as
>   suggested by Randy.
> ---
>  drivers/usb/typec/tcpm/Kconfig       |   5 +
>  drivers/usb/typec/tcpm/Makefile      |  13 +-
>  drivers/usb/typec/tcpm/tcpci.h       |   1 +
>  drivers/usb/typec/tcpm/tcpci_maxim.c | 474 +++++++++++++++++++++++++++
>  4 files changed, 487 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c
> 
> diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
> index fa3f39336246..dd0d446a4613 100644
> --- a/drivers/usb/typec/tcpm/Kconfig
> +++ b/drivers/usb/typec/tcpm/Kconfig
> @@ -27,6 +27,11 @@ config TYPEC_RT1711H
>  	  Type-C Port Controller Manager to provide USB PD and USB
>  	  Type-C functionalities.
>  
> +config TYPEC_TCPCI_MAXIM
> +	tristate "Maxim TCPCI based Type-C chip driver"
> +	help
> +	  MAXIM TCPCI based Type-C chip driver.
> +
>  endif # TYPEC_TCPCI
>  
>  config TYPEC_FUSB302
> diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
> index a5ff6c8eb892..58d001cf0dd2 100644
> --- a/drivers/usb/typec/tcpm/Makefile
> +++ b/drivers/usb/typec/tcpm/Makefile
> @@ -1,7 +1,8 @@
>  # SPDX-License-Identifier: GPL-2.0
> -obj-$(CONFIG_TYPEC_TCPM)	+= tcpm.o
> -obj-$(CONFIG_TYPEC_FUSB302)	+= fusb302.o
> -obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
> -typec_wcove-y			:= wcove.o
> -obj-$(CONFIG_TYPEC_TCPCI)	+= tcpci.o
> -obj-$(CONFIG_TYPEC_RT1711H)	+= tcpci_rt1711h.o
> +obj-$(CONFIG_TYPEC_TCPM)		+= tcpm.o
> +obj-$(CONFIG_TYPEC_FUSB302)		+= fusb302.o
> +obj-$(CONFIG_TYPEC_WCOVE)		+= typec_wcove.o
> +typec_wcove-y				:= wcove.o
> +obj-$(CONFIG_TYPEC_TCPCI)		+= tcpci.o
> +obj-$(CONFIG_TYPEC_RT1711H)		+= tcpci_rt1711h.o
> +obj-$(CONFIG_TYPEC_TCPCI_MAXIM)		+= tcpci_maxim.o
> diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> index 4d441bdf24d5..82f021a82456 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -109,6 +109,7 @@
>  
>  #define TCPC_RX_BYTE_CNT		0x30
>  #define TCPC_RX_BUF_FRAME_TYPE		0x31
> +#define TCPC_RX_BUF_FRAME_TYPE_SOP	0
>  #define TCPC_RX_HDR			0x32
>  #define TCPC_RX_DATA			0x34 /* through 0x4f */
>  
> diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
> new file mode 100644
> index 000000000000..b61f290a8f96
> --- /dev/null
> +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
> @@ -0,0 +1,474 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020, Google LLC
> + *
> + * MAXIM TCPCI based TCPC driver
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/regmap.h>
> +#include <linux/usb/pd.h>
> +#include <linux/usb/tcpm.h>
> +#include <linux/usb/typec.h>
> +
> +#include "tcpci.h"
> +
> +#define PD_ACTIVITY_TIMEOUT_MS				10000
> +
> +#define TCPC_VENDOR_ALERT				0x80
> +
> +#define TCPC_RECEIVE_BUFFER_COUNT_OFFSET		0
> +#define TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET		1
> +#define TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET		2
> +
> +/*
> + * LongMessage not supported, hence 32 bytes for buf to be read from RECEIVE_BUFFER.
> + * DEVICE_CAPABILITIES_2.LongMessage = 0, the value in READABLE_BYTE_COUNT reg shall be
> + * less than or equal to 31. Since, RECEIVE_BUFFER len = 31 + 1(READABLE_BYTE_COUNT).
> + */
> +#define TCPC_RECEIVE_BUFFER_LEN				32
> +
> +#define MAX_BUCK_BOOST_SID				0x69
> +#define MAX_BUCK_BOOST_OP				0xb9
> +#define MAX_BUCK_BOOST_OFF				0
> +#define MAX_BUCK_BOOST_SOURCE				0xa
> +#define MAX_BUCK_BOOST_SINK				0x5
> +
> +struct max_tcpci_chip {
> +	struct tcpci_data data;
> +	struct tcpci *tcpci;
> +	struct device *dev;
> +	struct i2c_client *client;
> +	struct tcpm_port *port;
> +};
> +
> +static const struct regmap_range max_tcpci_tcpci_range[] = {
> +	regmap_reg_range(0x00, 0x95)
> +};
> +
> +const struct regmap_access_table max_tcpci_tcpci_write_table = {
> +	.yes_ranges = max_tcpci_tcpci_range,
> +	.n_yes_ranges = ARRAY_SIZE(max_tcpci_tcpci_range),
> +};
> +
> +static const struct regmap_config max_tcpci_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = 0x95,
> +	.wr_table = &max_tcpci_tcpci_write_table,
> +};
> +
> +static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata)
> +{
> +	return container_of(tdata, struct max_tcpci_chip, data);
> +}
> +
> +static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
> +{
> +	return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
> +}
> +
> +static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
> +{
> +	return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
> +}
> +
> +static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
> +{
> +	return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
> +}
> +
> +static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
> +{
> +	return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
> +}
> +
> +static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
> +{
> +	u16 alert_mask = 0;
> +	int ret;
> +
> +	ret = max_tcpci_write16(chip, TCPC_ALERT, 0xffff);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "Error writing to TCPC_ALERT ret:%d\n", ret);
> +		return;
> +	}
> +
> +	ret = max_tcpci_write16(chip, TCPC_VENDOR_ALERT, 0xffff);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "Error writing to TCPC_VENDOR_ALERT 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;
> +
> +	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);
> +		return;
> +	}
> +
> +	/* Enable vbus voltage monitoring and voltage alerts */
> +	ret = max_tcpci_write8(chip, TCPC_POWER_CTRL, 0);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "Error writing to TCPC_POWER_CTRL ret:%d\n", ret);
> +		return;
> +	}
> +}
> +
> +static void process_rx(struct max_tcpci_chip *chip, u16 status)
> +{
> +	struct pd_message msg;
> +	u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN];
> +	int ret, payload_index;
> +	u8 *rx_buf_ptr;
> +
> +	/*
> +	 * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_x registers
> +	 * plus one (for the RX_BUF_FRAME_TYPE) Table 4-36.
> +	 * Read the count and frame type.
> +	 */
> +	ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, 2);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "TCPC_RX_BYTE_CNT read failed ret:%d", ret);
> +		return;
> +	}
> +
> +	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) {
> +		max_tcpci_write16(chip, 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 (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
> +		dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d", count);
> +		return;
> +	}
> +
> +	/*
> +	 * Read count + 1 as RX_BUF_BYTE_x is hidden and can only be read through
> +	 * TCPC_RX_BYTE_CNT
> +	 */
> +	count += 1;
> +	ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, count);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "Error: TCPC_RX_BYTE_CNT read failed: %d", ret);
> +		return;
> +	}
> +
> +	rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
> +	msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
> +	rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
> +	for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
> +	     rx_buf_ptr += sizeof(msg.payload[0]))
> +		msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);
> +
> +	/*
> +	 * Read complete, clear RX status alert bit.
> +	 * Clear overflow as well if set.
> +	 */
> +	ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
> +				TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
> +				TCPC_ALERT_RX_STATUS);
> +	if (ret < 0)
> +		return;
> +
> +	tcpm_pd_receive(chip->port, &msg);
> +}
> +
> +static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink)
> +{
> +	struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
> +	u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE};
> +	u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK};
> +	u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF};
> +	struct i2c_client *i2c = chip->client;
> +	int ret;
> +
> +	struct i2c_msg msgs[] = {
> +		{
> +			.addr = MAX_BUCK_BOOST_SID,
> +			.flags = i2c->flags & I2C_M_TEN,
> +			.len = 2,
> +			.buf = source ? buffer_source : sink ? buffer_sink : buffer_none,
> +		},
> +	};
> +
> +	if (source && sink) {
> +		dev_err(chip->dev, "Both source and sink set\n");
> +		return -EINVAL;
> +	}

So can buffer_none ever be used?

> +	ret = i2c_transfer(i2c->adapter, msgs, 1);
> +
> +	return  ret < 0 ? ret : 1;
> +}
> +
> +static void process_power_status(struct max_tcpci_chip *chip)
> +{
> +	u8 pwr_status;
> +	int ret;
> +
> +	ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &pwr_status);
> +	if (ret < 0)
> +		return;
> +
> +	if (pwr_status == 0xff)
> +		max_tcpci_init_regs(chip);
> +	else
> +		tcpm_vbus_change(chip->port);
> +}
> +
> +static void process_tx(struct max_tcpci_chip *chip, u16 status)
> +{
> +	if (status & TCPC_ALERT_TX_SUCCESS)
> +		tcpm_pd_transmit_complete(chip->port, TCPC_TX_SUCCESS);
> +	else if (status & TCPC_ALERT_TX_DISCARDED)
> +		tcpm_pd_transmit_complete(chip->port, TCPC_TX_DISCARDED);
> +	else if (status & TCPC_ALERT_TX_FAILED)
> +		tcpm_pd_transmit_complete(chip->port, TCPC_TX_FAILED);
> +
> +	/* Reinit regs as Hard reset sets them to default value */
> +	if ((status & TCPC_ALERT_TX_SUCCESS) && (status & TCPC_ALERT_TX_FAILED))
> +		max_tcpci_init_regs(chip);
> +}
> +
> +static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
> +{
> +	u16 mask;
> +	int ret;
> +
> +	/*
> +	 * Clear alert status for everything except RX_STATUS, which shouldn't
> +	 * be cleared until we have successfully retrieved message.
> +	 */
> +	if (status & ~TCPC_ALERT_RX_STATUS) {
> +		mask = status & TCPC_ALERT_RX_BUF_OVF ?
> +			status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
> +			status & ~TCPC_ALERT_RX_STATUS;
> +		ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
> +		if (ret < 0) {
> +			dev_err(chip->dev, "ALERT clear failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	if (status & TCPC_ALERT_RX_BUF_OVF && !(status & TCPC_ALERT_RX_STATUS)) {
> +		ret = max_tcpci_write16(chip, TCPC_ALERT, (TCPC_ALERT_RX_STATUS |
> +							  TCPC_ALERT_RX_BUF_OVF));
> +		if (ret < 0) {
> +			dev_err(chip->dev, "ALERT clear failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	if (status & TCPC_ALERT_RX_STATUS)
> +		process_rx(chip, status);
> +
> +	if (status & TCPC_ALERT_TX_DISCARDED)
> +		dev_info(chip->dev, "TX_DISCARDED");

What does that mean? Is it relevant for the user?

> +
> +	if (status & TCPC_ALERT_VBUS_DISCNCT)
> +		tcpm_vbus_change(chip->port);
> +
> +	if (status & TCPC_ALERT_CC_STATUS)
> +		tcpm_cc_change(chip->port);
> +
> +	if (status & TCPC_ALERT_POWER_STATUS)
> +		process_power_status(chip);
> +
> +	if (status & TCPC_ALERT_RX_HARD_RST) {
> +		tcpm_pd_hard_reset(chip->port);
> +		max_tcpci_init_regs(chip);
> +	}
> +
> +	if (status & TCPC_ALERT_TX_SUCCESS || status & TCPC_ALERT_TX_DISCARDED || status &
> +	    TCPC_ALERT_TX_FAILED)
> +		process_tx(chip, status);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
> +{
> +	struct max_tcpci_chip *chip = dev_id;
> +	u16 status;
> +	irqreturn_t irq_return;
> +	int ret;
> +
> +	if (!chip->port)
> +		return IRQ_HANDLED;
> +
> +	ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
> +	if (ret < 0) {
> +		dev_err(chip->dev, "ALERT read failed\n");
> +		return ret;
> +	}
> +	while (status) {
> +		irq_return = _max_tcpci_irq(chip, status);
> +		/* Do not return if the ALERT is already set. */
> +		ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
> +		if (ret < 0)
> +			break;
> +	}
> +
> +	return irq_return;
> +}
> +
> +static irqreturn_t max_tcpci_isr(int irq, void *dev_id)
> +{
> +	struct max_tcpci_chip *chip = dev_id;
> +
> +	pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS);
> +
> +	if (!chip->port)
> +		return IRQ_HANDLED;
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client)
> +{
> +	int ret, irq_gpio;
> +
> +	irq_gpio = of_get_named_gpio(client->dev.of_node, "usbpd,usbpd_int", 0);
> +	client->irq = gpio_to_irq(irq_gpio);
> +	if (!client->irq)
> +		return -ENODEV;
> +
> +	ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
> +					(IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
> +					chip);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	enable_irq_wake(client->irq);
> +	return 0;
> +}
> +
> +static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata,
> +				    enum typec_cc_status cc)
> +{
> +	struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
> +
> +	max_tcpci_init_regs(chip);
> +
> +	return 0;
> +}
> +
> +static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
> +{
> +	/*
> +	 * Generic TCPCI overwrites the regs once this driver initializes
> +	 * them. Prevent this by returning -1.
> +	 */
> +	return -1;
> +}
> +
> +static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)
> +{
> +	int ret;
> +	struct max_tcpci_chip *chip;
> +	u8 power_status;
> +
> +	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> +	if (!chip)
> +		return -ENOMEM;
> +
> +	chip->client = client;
> +	chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
> +	if (IS_ERR(chip->data.regmap)) {
> +		dev_err(&client->dev, "Regmap init failed\n");
> +		return PTR_ERR(chip->data.regmap);
> +	}
> +
> +	chip->dev = &client->dev;
> +	i2c_set_clientdata(client, chip);
> +
> +	ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (power_status & TCPC_POWER_STATUS_UNINIT) {
> +		dev_err(&client->dev, "TCPC not ready!");
> +		return -EPROBE_DEFER;
> +	}

That looks wrong. There is no guarantee that this wasn't the last
device that is registered for a while. Or is there?

I think you should consider TCPC_POWER_STATUS_UNINIT in tcpci_init(),
just like tcpci.c does. Or is there some reason why you are checking
it here?

> +
> +	/* Chip level tcpci callbacks */
> +	chip->data.set_vbus = max_tcpci_set_vbus;
> +	chip->data.start_drp_toggling = max_tcpci_start_toggling;
> +	chip->data.TX_BUF_BYTE_x_hidden = true;
> +	chip->data.init = tcpci_init;
> +
> +	max_tcpci_init_regs(chip);
> +	chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
> +	if (IS_ERR_OR_NULL(chip->tcpci)) {
> +		dev_err(&client->dev, "TCPCI port registration failed");
> +		ret = PTR_ERR(chip->tcpci);
> +		return PTR_ERR(chip->tcpci);
> +	}
> +	chip->port = tcpci_get_tcpm_port(chip->tcpci);
> +	ret = max_tcpci_init_alert(chip, client);
> +	if (ret < 0)
> +		goto unreg_port;
> +
> +	device_init_wakeup(chip->dev, true);
> +	return 0;
> +
> +unreg_port:
> +	tcpci_unregister_port(chip->tcpci);
> +
> +	return ret;
> +}
> +
> +static int max_tcpci_remove(struct i2c_client *client)
> +{
> +	struct max_tcpci_chip *chip = i2c_get_clientdata(client);
> +
> +	if (!IS_ERR_OR_NULL(chip->tcpci))
> +		tcpci_unregister_port(chip->tcpci);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id max_tcpci_id[] = {
> +	{ "maxtcpc", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id max_tcpci_of_match[] = {
> +	{ .compatible = "maxim,tcpc", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, max_tcpci_of_match);
> +#endif
> +
> +static struct i2c_driver max_tcpci_i2c_driver = {
> +	.driver = {
> +		.name = "maxtcpc",
> +		.of_match_table = of_match_ptr(max_tcpci_of_match),
> +	},
> +	.probe = max_tcpci_probe,
> +	.remove = max_tcpci_remove,
> +	.id_table = max_tcpci_id,
> +};
> +module_i2c_driver(max_tcpci_i2c_driver);
> +
> +MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
> +MODULE_DESCRIPTION("Maxim TCPCI based USB Type-C Port Controller Interface Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

thanks,

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS)
  2020-09-01  2:59 ` [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS) Badhri Jagan Sridharan
@ 2020-09-15 13:20   ` Heikki Krogerus
  2020-09-17 10:23     ` Badhri Jagan Sridharan
  0 siblings, 1 reply; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 13:20 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:22PM -0700, Badhri Jagan Sridharan wrote:
> PD 3.0 spec defines a new mechanism for power role swap called
> Fast role swap. This change enables TCPM to support FRS when
> acting as sink.
> 
> Once the explicit contract is negotiated, sink port is
> expected to query the source port for sink caps to
> determine whether the source is FRS capable.
> Bits 23 & 24 of fixed pdo of the sink caps from the source, when
> set, indicates the current needed by the source when fast role
> swap is in progress(Implicit contract phasae). 0 indicates that
> the source does not support Fast Role Swap.
> 
> Upon receiving the FRS signal from the source,
> TCPC(TCPM_FRS_EVENT) informs TCPM to start the Fast role swap sequence.
> 
> 1. TCPM sends FRS PD message: FR_SWAP_SEND
> 2. If response is not received within the expiry of
>    SenderResponseTimer, Error recovery is triggered.:
>    FR_SWAP_SEND_TIMEOUT
> 3. Upon receipt of the accept message, TCPM waits for
>    PSSourceOffTimer for PS_READY message from the partner:
>    FR_SWAP_SNK_SRC_NEW_SINK_READY.
> 
> TCPC is expected to autonomously turn on vbus once the FRS
> signal is received and vbus voltage falls below vsafe5v within
> tSrcFrSwap. This is different from traditional power role swap
> where the vbus sourcing is turned on by TCPM.
> 
> 4. By this time, TCPC most likely would have started to
>    source vbus, TCPM waits for tSrcFrSwap to see  if the
>    lower level TCPC driver signals TCPM_SOURCING_VBUS event:
>    FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED.
> 5. When TCPC signals sourcing vbus, TCPM sends PS_READY msg and
>    changes the CC pin from Rd to Rp. This is the end of fast
>    role swap sequence and TCPM initiates the sequnce to negotiate
>    explicit contract by transitioning into SRC_STARTUP after
>    SwapSrcStart.
> 
> The code is written based on the sequence described in "Figure 8-107:
> Dual-role Port in Sink to Source Fast Role Swap State Diagram" of
> USB Power Delivery Specification Revision 3.0, Version 1.2.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> - Rebased on top of usb-next and resolved conflicts due to the below
>   changes:
>   3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
>   6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> - enable_frs sequence is now run as part of the same kthread that runs
>   the state machines.
> - Fixed the implicit fallthrough warning in the switch case for
>   FR_SWAP_CANCEL case.
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 217 +++++++++++++++++++++++++++++++++-
>  include/linux/usb/pd.h        |  19 +--
>  include/linux/usb/tcpm.h      |   8 +-
>  include/linux/usb/typec.h     |  13 ++
>  4 files changed, 245 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index 92806547f485..083e7af107b2 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -106,6 +106,13 @@
>  	S(VCONN_SWAP_TURN_ON_VCONN),		\
>  	S(VCONN_SWAP_TURN_OFF_VCONN),		\
>  						\
> +	S(FR_SWAP_SEND),			\
> +	S(FR_SWAP_SEND_TIMEOUT),		\
> +	S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF),			\
> +	S(FR_SWAP_SNK_SRC_NEW_SINK_READY),		\
> +	S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED),	\
> +	S(FR_SWAP_CANCEL),			\
> +						\
>  	S(SNK_TRY),				\
>  	S(SNK_TRY_WAIT),			\
>  	S(SNK_TRY_WAIT_DEBOUNCE),               \
> @@ -127,6 +134,9 @@
>  	S(GET_PPS_STATUS_SEND),			\
>  	S(GET_PPS_STATUS_SEND_TIMEOUT),		\
>  						\
> +	S(GET_SINK_CAP),			\
> +	S(GET_SINK_CAP_TIMEOUT),		\
> +						\
>  	S(ERROR_RECOVERY),			\
>  	S(PORT_RESET),				\
>  	S(PORT_RESET_WAIT_OFF)
> @@ -175,6 +185,8 @@ enum adev_actions {
>  #define TCPM_CC_EVENT		BIT(0)
>  #define TCPM_VBUS_EVENT		BIT(1)
>  #define TCPM_RESET_EVENT	BIT(2)
> +#define TCPM_FRS_EVENT		BIT(3)
> +#define TCPM_SOURCING_VBUS	BIT(4)
>  
>  #define LOG_BUFFER_ENTRIES	1024
>  #define LOG_BUFFER_ENTRY_SIZE	128
> @@ -184,6 +196,8 @@ enum adev_actions {
>  #define SVID_DISCOVERY_MAX	16
>  #define ALTMODE_DISCOVERY_MAX	(SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
>  
> +#define GET_SINK_CAP_RETRY_MS	100
> +
>  struct pd_mode_data {
>  	int svid_index;		/* current SVID index		*/
>  	int nsvids;
> @@ -261,6 +275,8 @@ struct tcpm_port {
>  	struct kthread_work state_machine;
>  	struct hrtimer vdm_state_machine_timer;
>  	struct kthread_work vdm_state_machine;
> +	struct hrtimer enable_frs_timer;
> +	struct kthread_work enable_frs;
>  	bool state_machine_running;
>  
>  	struct completion tx_complete;
> @@ -335,6 +351,12 @@ struct tcpm_port {
>  	/* port belongs to a self powered device */
>  	bool self_powered;
>  
> +	/* FRS */
> +	enum frs_typec_current frs_current;

Do you use this member anywhere?

I can't see anywhere it being used, but I think you should use it
instead the one in port_caps.

> +	/* Sink caps have been queried */
> +	bool sink_cap_done;
> +
>  #ifdef CONFIG_DEBUG_FS
>  	struct dentry *dentry;
>  	struct mutex logbuffer_lock;	/* log buffer access lock */
> @@ -940,6 +962,16 @@ static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
>  	}
>  }

<snip>

> diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> index 9cb1bec94b71..2ec7451a14ab 100644
> --- a/include/linux/usb/typec.h
> +++ b/include/linux/usb/typec.h
> @@ -204,6 +204,18 @@ struct typec_operations {
>  			     enum typec_port_type type);
>  };
>  
> +/*
> + * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
> + * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
> + * Version 1.2"
> + */
> +enum frs_typec_current {
> +	FRS_NOT_SUPPORTED,
> +	FRS_DEFAULT_POWER,
> +	FRS_5V_1P5A,
> +	FRS_5V_3A,
> +};
> +
>  /*
>   * struct typec_capability - USB Type-C Port Capabilities
>   * @type: Supported power role of the port
> @@ -226,6 +238,7 @@ struct typec_capability {
>  	int			prefer_role;
>  	enum typec_accessory	accessory[TYPEC_MAX_ACCESSORY];
>  	unsigned int		orientation_aware:1;
> +	enum frs_typec_current	frs_current;

So forget about this. We can add it here if/when there is another
driver that needs that. You already have that member in struct
tcpm_port, so just use that for now.

And you can also introduce enum frs_typec_current in tcpm.c for now.

>  	struct fwnode_handle	*fwnode;
>  	void			*driver_data;
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

thanks,

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS
  2020-09-01  2:59 ` [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS Badhri Jagan Sridharan
@ 2020-09-15 13:23   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 13:23 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:23PM -0700, Badhri Jagan Sridharan wrote:
> Implement tcpc.enable_frs to enable TCPC to receive
> Fast role swap signal.
> 
> Additionally set the sink disconnect threshold to 4v
> to prevent disconnect during Fast Role swap.
> 
> Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>

One nitpick below. Otherwise, FWIW:

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> ---
>  drivers/usb/typec/tcpm/tcpci.c | 17 +++++++++++++++++
>  drivers/usb/typec/tcpm/tcpci.h |  8 ++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> index d6a6fac82d48..f9f0af64da5f 100644
> --- a/drivers/usb/typec/tcpm/tcpci.c
> +++ b/drivers/usb/typec/tcpm/tcpci.c
> @@ -268,6 +268,22 @@ static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
>  				enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
>  }
>  
> +static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
> +{
> +	struct tcpci *tcpci = tcpc_to_tcpci(dev);
> +	int ret;
> +
> +	/* To prevent disconnect during FRS, set disconnect threshold to 3.5V */
> +	ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ?
> +				 TCPC_FAST_ROLE_SWAP_EN : 0);

	ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN,
                                 enable ? TCPC_FAST_ROLE_SWAP_EN : 0);

> +
> +	return ret;
> +}
> +
>  static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
>  {
>  	struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
> @@ -611,6 +627,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
>  	tcpci->tcpc.set_roles = tcpci_set_roles;
>  	tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
>  	tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
> +	tcpci->tcpc.enable_frs = tcpci_enable_frs;
>  
>  	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 82f021a82456..5ef07a56d67a 100644
> --- a/drivers/usb/typec/tcpm/tcpci.h
> +++ b/drivers/usb/typec/tcpm/tcpci.h
> @@ -16,6 +16,7 @@
>  #define TCPC_PD_INT_REV			0xa
>  
>  #define TCPC_ALERT			0x10
> +#define TCPC_ALERT_EXTND		BIT(14)
>  #define TCPC_ALERT_EXTENDED_STATUS	BIT(13)
>  #define TCPC_ALERT_VBUS_DISCNCT		BIT(11)
>  #define TCPC_ALERT_RX_BUF_OVF		BIT(10)
> @@ -37,6 +38,9 @@
>  #define TCPC_EXTENDED_STATUS_MASK		0x16
>  #define TCPC_EXTENDED_STATUS_MASK_VSAFE0V	BIT(0)
>  
> +#define TCPC_ALERT_EXTENDED_MASK	0x17
> +#define TCPC_SINK_FAST_ROLE_SWAP	BIT(0)
> +
>  #define TCPC_CONFIG_STD_OUTPUT		0x18
>  
>  #define TCPC_TCPC_CTRL			0x19
> @@ -63,6 +67,7 @@
>  
>  #define TCPC_POWER_CTRL			0x1c
>  #define TCPC_POWER_CTRL_VCONN_ENABLE	BIT(0)
> +#define TCPC_FAST_ROLE_SWAP_EN		BIT(7)
>  
>  #define TCPC_CC_STATUS			0x1d
>  #define TCPC_CC_STATUS_TOGGLING		BIT(5)
> @@ -74,11 +79,14 @@
>  
>  #define TCPC_POWER_STATUS		0x1e
>  #define TCPC_POWER_STATUS_UNINIT	BIT(6)
> +#define TCPC_POWER_STATUS_SOURCING_VBUS	BIT(4)
>  #define TCPC_POWER_STATUS_VBUS_DET	BIT(3)
>  #define TCPC_POWER_STATUS_VBUS_PRES	BIT(2)
>  
>  #define TCPC_FAULT_STATUS		0x1f
>  
> +#define TCPC_ALERT_EXTENDED		0x21
> +
>  #define TCPC_COMMAND			0x23
>  #define TCPC_CMD_WAKE_I2C		0x11
>  #define TCPC_CMD_DISABLE_VBUS_DETECT	0x22
> -- 
> 2.28.0.402.g5ffc5be6b7-goog

thanks,

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS
  2020-09-01  2:59 ` [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS Badhri Jagan Sridharan
@ 2020-09-15 13:25   ` Heikki Krogerus
  0 siblings, 0 replies; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 13:25 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:24PM -0700, Badhri Jagan Sridharan wrote:
> 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 <badhri@google.com>

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> ---
> Changes since v1:
> - Changing patch version to v6 to fix version number confusion.
> ---
>  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 b61f290a8f96..6ba808ad901a 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.402.g5ffc5be6b7-goog

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (14 preceding siblings ...)
  2020-09-15 12:09 ` [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Heikki Krogerus
@ 2020-09-15 13:33 ` Heikki Krogerus
  2020-09-15 16:59   ` Badhri Jagan Sridharan
  2020-09-16 11:05 ` Greg Kroah-Hartman
  16 siblings, 1 reply; 32+ messages in thread
From: Heikki Krogerus @ 2020-09-15 13:33 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Greg Kroah-Hartman, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:13PM -0700, Badhri Jagan Sridharan wrote:
> First of all apologies for mixing up the patch version as noted by
> Heikki and Greg. All of them were v1's but since I was manually adding
> the version numbers I mixed them up. Using the --reroll-count option
> now. Updating the patch version to v6 (highest version number in the
> previous patchset + 1) to avoid confusion.
> 
> I also rebased on to off of the recent usb-next tip:
> 5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
> Which had the following changes causing merge conflict:
> 3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> 6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> 
> Addressed comments from Heikki and Randy which have described in the
> individual commit's change history as well.

I'll try to study the AutoDischarge a bit before reviewing the last
patches. They all appeared to be only about AutoDischarge. Sorry, I
didn't have time for that yet. If Guenter is fine with those, then
feel free to add my ACK to those patches. But Guenter really should
review these in any case. Hope he has time.

Br,

-- 
heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
  2020-09-15 12:09 ` [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Heikki Krogerus
@ 2020-09-15 16:57   ` Badhri Jagan Sridharan
  0 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-15 16:57 UTC (permalink / raw)
  To: Heikki Krogerus; +Cc: Guenter Roeck, Greg Kroah-Hartman, USB, LKML

On Tue, Sep 15, 2020 at 5:09 AM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> On Mon, Aug 31, 2020 at 07:59:13PM -0700, Badhri Jagan Sridharan wrote:
> > First of all apologies for mixing up the patch version as noted by
> > Heikki and Greg. All of them were v1's but since I was manually adding
> > the version numbers I mixed them up. Using the --reroll-count option
> > now. Updating the patch version to v6 (highest version number in the
> > previous patchset + 1) to avoid confusion.
>
> If this is v6, then where are v2 - v5? And what changed? Why didn't
> you just make this v2?

Frankly, I did not know how to fix the version numbers that I messed
up in the original
patchset. I had, by mistake, versioned the patch5 in the series v5 in
the original patchset.
So I thought I will consistently call them V6 and update all the patches
to version v6 to avoid confusion. To confirm there is no v2-v5 for most of them.
I have also mentioned the actual code changes and versioning changes in the
change history for each patch. Hopefully that mitigates the confusion.
Again apologies for messing up the versioning in the original patchset !
I have started to double check the patch version numbers before sending.

Thanks,
Badhri

>
>
> > I also rebased on to off of the recent usb-next tip:
> > 5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
> > Which had the following changes causing merge conflict:
> > 3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> > 6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> >
> > Addressed comments from Heikki and Randy which have described in the
> > individual commit's change history as well.
> >
> > Badhri Jagan Sridharan (14):
> >   usb: typec: tcpci: Add register definitions to tcpci
> >   usb: typec: tcpci: Add support when hidden tx registers are
> >     inaccessible
> >   usb: typec: tcpci: update ROLE_CONTROL for DRP
> >   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: Implement enabling Auto Discharge disconnect support
> >   usb: typec: tcpci: Implement Auto discharge disconnect callbacks
> >   usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold
> >
> >  .../bindings/connector/usb-connector.txt      | 128 ++++
> >  .../devicetree/bindings/usb/maxim,tcpci.txt   |  44 ++
> >  drivers/usb/typec/tcpm/Kconfig                |   5 +
> >  drivers/usb/typec/tcpm/Makefile               |  13 +-
> >  drivers/usb/typec/tcpm/tcpci.c                | 146 ++++-
> >  drivers/usb/typec/tcpm/tcpci.h                |  43 ++
> >  drivers/usb/typec/tcpm/tcpci_maxim.c          | 564 ++++++++++++++++++
> >  drivers/usb/typec/tcpm/tcpm.c                 | 291 ++++++++-
> >  include/dt-bindings/usb/pd.h                  |  10 +
> >  include/linux/usb/pd.h                        |  19 +-
> >  include/linux/usb/tcpm.h                      |  24 +-
> >  include/linux/usb/typec.h                     |  13 +
> >  12 files changed, 1266 insertions(+), 34 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/connector/usb-connector.txt
> >  create mode 100644 Documentation/devicetree/bindings/usb/maxim,tcpci.txt
> >  create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c
> >
> >
> > base-commit: 5fedf0d295d3ef69fd85fdee4cb68fd3756b54c2
> > --
> > 2.28.0.402.g5ffc5be6b7-goog
>
> --
> heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
  2020-09-15 13:33 ` Heikki Krogerus
@ 2020-09-15 16:59   ` Badhri Jagan Sridharan
  0 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-15 16:59 UTC (permalink / raw)
  To: Heikki Krogerus; +Cc: Guenter Roeck, Greg Kroah-Hartman, USB, LKML

On Tue, Sep 15, 2020 at 6:33 AM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> On Mon, Aug 31, 2020 at 07:59:13PM -0700, Badhri Jagan Sridharan wrote:
> > First of all apologies for mixing up the patch version as noted by
> > Heikki and Greg. All of them were v1's but since I was manually adding
> > the version numbers I mixed them up. Using the --reroll-count option
> > now. Updating the patch version to v6 (highest version number in the
> > previous patchset + 1) to avoid confusion.
> >
> > I also rebased on to off of the recent usb-next tip:
> > 5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
> > Which had the following changes causing merge conflict:
> > 3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> > 6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> >
> > Addressed comments from Heikki and Randy which have described in the
> > individual commit's change history as well.
>
> I'll try to study the AutoDischarge a bit before reviewing the last
> patches. They all appeared to be only about AutoDischarge. Sorry, I
> didn't have time for that yet. If Guenter is fine with those, then
> feel free to add my ACK to those patches. But Guenter really should
> review these in any case. Hope he has time.

Really appreciate you spending time on reviewing the patches. So
thanks for doing that.
I quickly went through the comments you have given. Will respond today.

Thanks & Regards,
Badhri

>
> Br,
>
> --
> heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback
  2020-09-15 12:22   ` Heikki Krogerus
@ 2020-09-16  9:07     ` Badhri Jagan Sridharan
  0 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-16  9:07 UTC (permalink / raw)
  To: Heikki Krogerus; +Cc: Guenter Roeck, Greg Kroah-Hartman, USB, LKML

On Tue, Sep 15, 2020 at 5:22 AM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> On Mon, Aug 31, 2020 at 07:59:18PM -0700, Badhri Jagan Sridharan wrote:
> > set_vbus callback allows TCPC which are TCPCI based, however,
> > does not support turning on sink and source mode through
> > Command.SinkVbus and Command.SourceVbusDefaultVoltage.
> >
> > Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> > ---
> > Changes since v1:
> > - Changing patch version to v6 to fix version number confusion.
> > ---
> >  drivers/usb/typec/tcpm/tcpci.c | 7 +++++++
> >  drivers/usb/typec/tcpm/tcpci.h | 1 +
> >  2 files changed, 8 insertions(+)
> >
> > diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
> > index b960fe5a0f28..d6a6fac82d48 100644
> > --- a/drivers/usb/typec/tcpm/tcpci.c
> > +++ b/drivers/usb/typec/tcpm/tcpci.c
> > @@ -328,6 +328,13 @@ static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
> >       struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
> >       int ret;
> >
> > +     if (tcpci->data->set_vbus) {
> > +             ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink);
> > +             /* Bypass when ret > 0 */
> > +             if (ret != 0)
> > +                     return ret < 0 ? ret : 0;
>
> Can it return positive value? What does positive value mean?

When a positive value is returned, tcpci->data->set_vbus is indicating
the tcpci.c code to bypass
other commands here. i.e. tcpci->data->set_vbus completely handles
sinking/sourcing vbus and
does not want tcpc.c to execute any other TCPC_CMD_* command.
Open to suggestions if you are not happy with this. All I want is to
make tcpci code pass on the call
from TCPM to chip level TCPC driver (i.e.  tcpci_maxim.c in this case)
and not do anything else.

>
> > +     }
> > +
> >       /* Disable both source and sink first before enabling anything */
> >
> >       if (!source) {
> > diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> > index 04c49a0b0368..4d441bdf24d5 100644
> > --- a/drivers/usb/typec/tcpm/tcpci.h
> > +++ b/drivers/usb/typec/tcpm/tcpci.h
> > @@ -144,6 +144,7 @@ struct tcpci_data {
> >                        bool enable);
> >       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);
> >  };
> >
> >  struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
> > --
> > 2.28.0.402.g5ffc5be6b7-goog
>
> --
> heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver
  2020-09-15 12:43   ` Heikki Krogerus
@ 2020-09-16  9:34     ` Badhri Jagan Sridharan
  0 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-16  9:34 UTC (permalink / raw)
  To: Heikki Krogerus; +Cc: Guenter Roeck, Greg Kroah-Hartman, USB, LKML

On Tue, Sep 15, 2020 at 5:44 AM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> On Mon, Aug 31, 2020 at 07:59:20PM -0700, Badhri Jagan Sridharan wrote:
> > Chip level TCPC driver for Maxim's TCPCI implementation.
> > This TCPC implementation does not support the following
> > commands: COMMAND.SinkVbus, COMMAND.SourceVbusDefaultVoltage,
> > COMMAND.SourceVbusHighVoltage. Instead the sinking and sourcing
> > from vbus is supported by writes to custom registers.
> >
> > Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> > ---
> > Changes since v1:
> > - Changing patch version to v6 to fix version number confusion.
> > - Removed setting USB_PSY and terminating description with period as
> >   suggested by Randy.
> > ---
> >  drivers/usb/typec/tcpm/Kconfig       |   5 +
> >  drivers/usb/typec/tcpm/Makefile      |  13 +-
> >  drivers/usb/typec/tcpm/tcpci.h       |   1 +
> >  drivers/usb/typec/tcpm/tcpci_maxim.c | 474 +++++++++++++++++++++++++++
> >  4 files changed, 487 insertions(+), 6 deletions(-)
> >  create mode 100644 drivers/usb/typec/tcpm/tcpci_maxim.c
> >
> > diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
> > index fa3f39336246..dd0d446a4613 100644
> > --- a/drivers/usb/typec/tcpm/Kconfig
> > +++ b/drivers/usb/typec/tcpm/Kconfig
> > @@ -27,6 +27,11 @@ config TYPEC_RT1711H
> >         Type-C Port Controller Manager to provide USB PD and USB
> >         Type-C functionalities.
> >
> > +config TYPEC_TCPCI_MAXIM
> > +     tristate "Maxim TCPCI based Type-C chip driver"
> > +     help
> > +       MAXIM TCPCI based Type-C chip driver.
> > +
> >  endif # TYPEC_TCPCI
> >
> >  config TYPEC_FUSB302
> > diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
> > index a5ff6c8eb892..58d001cf0dd2 100644
> > --- a/drivers/usb/typec/tcpm/Makefile
> > +++ b/drivers/usb/typec/tcpm/Makefile
> > @@ -1,7 +1,8 @@
> >  # SPDX-License-Identifier: GPL-2.0
> > -obj-$(CONFIG_TYPEC_TCPM)     += tcpm.o
> > -obj-$(CONFIG_TYPEC_FUSB302)  += fusb302.o
> > -obj-$(CONFIG_TYPEC_WCOVE)    += typec_wcove.o
> > -typec_wcove-y                        := wcove.o
> > -obj-$(CONFIG_TYPEC_TCPCI)    += tcpci.o
> > -obj-$(CONFIG_TYPEC_RT1711H)  += tcpci_rt1711h.o
> > +obj-$(CONFIG_TYPEC_TCPM)             += tcpm.o
> > +obj-$(CONFIG_TYPEC_FUSB302)          += fusb302.o
> > +obj-$(CONFIG_TYPEC_WCOVE)            += typec_wcove.o
> > +typec_wcove-y                                := wcove.o
> > +obj-$(CONFIG_TYPEC_TCPCI)            += tcpci.o
> > +obj-$(CONFIG_TYPEC_RT1711H)          += tcpci_rt1711h.o
> > +obj-$(CONFIG_TYPEC_TCPCI_MAXIM)              += tcpci_maxim.o
> > diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
> > index 4d441bdf24d5..82f021a82456 100644
> > --- a/drivers/usb/typec/tcpm/tcpci.h
> > +++ b/drivers/usb/typec/tcpm/tcpci.h
> > @@ -109,6 +109,7 @@
> >
> >  #define TCPC_RX_BYTE_CNT             0x30
> >  #define TCPC_RX_BUF_FRAME_TYPE               0x31
> > +#define TCPC_RX_BUF_FRAME_TYPE_SOP   0
> >  #define TCPC_RX_HDR                  0x32
> >  #define TCPC_RX_DATA                 0x34 /* through 0x4f */
> >
> > diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
> > new file mode 100644
> > index 000000000000..b61f290a8f96
> > --- /dev/null
> > +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
> > @@ -0,0 +1,474 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020, Google LLC
> > + *
> > + * MAXIM TCPCI based TCPC driver
> > + */
> > +
> > +#include <linux/gpio.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/i2c.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of_gpio.h>
> > +#include <linux/regmap.h>
> > +#include <linux/usb/pd.h>
> > +#include <linux/usb/tcpm.h>
> > +#include <linux/usb/typec.h>
> > +
> > +#include "tcpci.h"
> > +
> > +#define PD_ACTIVITY_TIMEOUT_MS                               10000
> > +
> > +#define TCPC_VENDOR_ALERT                            0x80
> > +
> > +#define TCPC_RECEIVE_BUFFER_COUNT_OFFSET             0
> > +#define TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET                1
> > +#define TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET               2
> > +
> > +/*
> > + * LongMessage not supported, hence 32 bytes for buf to be read from RECEIVE_BUFFER.
> > + * DEVICE_CAPABILITIES_2.LongMessage = 0, the value in READABLE_BYTE_COUNT reg shall be
> > + * less than or equal to 31. Since, RECEIVE_BUFFER len = 31 + 1(READABLE_BYTE_COUNT).
> > + */
> > +#define TCPC_RECEIVE_BUFFER_LEN                              32
> > +
> > +#define MAX_BUCK_BOOST_SID                           0x69
> > +#define MAX_BUCK_BOOST_OP                            0xb9
> > +#define MAX_BUCK_BOOST_OFF                           0
> > +#define MAX_BUCK_BOOST_SOURCE                                0xa
> > +#define MAX_BUCK_BOOST_SINK                          0x5
> > +
> > +struct max_tcpci_chip {
> > +     struct tcpci_data data;
> > +     struct tcpci *tcpci;
> > +     struct device *dev;
> > +     struct i2c_client *client;
> > +     struct tcpm_port *port;
> > +};
> > +
> > +static const struct regmap_range max_tcpci_tcpci_range[] = {
> > +     regmap_reg_range(0x00, 0x95)
> > +};
> > +
> > +const struct regmap_access_table max_tcpci_tcpci_write_table = {
> > +     .yes_ranges = max_tcpci_tcpci_range,
> > +     .n_yes_ranges = ARRAY_SIZE(max_tcpci_tcpci_range),
> > +};
> > +
> > +static const struct regmap_config max_tcpci_regmap_config = {
> > +     .reg_bits = 8,
> > +     .val_bits = 8,
> > +     .max_register = 0x95,
> > +     .wr_table = &max_tcpci_tcpci_write_table,
> > +};
> > +
> > +static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata)
> > +{
> > +     return container_of(tdata, struct max_tcpci_chip, data);
> > +}
> > +
> > +static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
> > +{
> > +     return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
> > +}
> > +
> > +static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
> > +{
> > +     return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
> > +}
> > +
> > +static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
> > +{
> > +     return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
> > +}
> > +
> > +static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
> > +{
> > +     return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
> > +}
> > +
> > +static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
> > +{
> > +     u16 alert_mask = 0;
> > +     int ret;
> > +
> > +     ret = max_tcpci_write16(chip, TCPC_ALERT, 0xffff);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "Error writing to TCPC_ALERT ret:%d\n", ret);
> > +             return;
> > +     }
> > +
> > +     ret = max_tcpci_write16(chip, TCPC_VENDOR_ALERT, 0xffff);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "Error writing to TCPC_VENDOR_ALERT 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;
> > +
> > +     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);
> > +             return;
> > +     }
> > +
> > +     /* Enable vbus voltage monitoring and voltage alerts */
> > +     ret = max_tcpci_write8(chip, TCPC_POWER_CTRL, 0);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "Error writing to TCPC_POWER_CTRL ret:%d\n", ret);
> > +             return;
> > +     }
> > +}
> > +
> > +static void process_rx(struct max_tcpci_chip *chip, u16 status)
> > +{
> > +     struct pd_message msg;
> > +     u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN];
> > +     int ret, payload_index;
> > +     u8 *rx_buf_ptr;
> > +
> > +     /*
> > +      * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_x registers
> > +      * plus one (for the RX_BUF_FRAME_TYPE) Table 4-36.
> > +      * Read the count and frame type.
> > +      */
> > +     ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, 2);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "TCPC_RX_BYTE_CNT read failed ret:%d", ret);
> > +             return;
> > +     }
> > +
> > +     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) {
> > +             max_tcpci_write16(chip, 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 (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
> > +             dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d", count);
> > +             return;
> > +     }
> > +
> > +     /*
> > +      * Read count + 1 as RX_BUF_BYTE_x is hidden and can only be read through
> > +      * TCPC_RX_BYTE_CNT
> > +      */
> > +     count += 1;
> > +     ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, count);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "Error: TCPC_RX_BYTE_CNT read failed: %d", ret);
> > +             return;
> > +     }
> > +
> > +     rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
> > +     msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
> > +     rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
> > +     for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
> > +          rx_buf_ptr += sizeof(msg.payload[0]))
> > +             msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);
> > +
> > +     /*
> > +      * Read complete, clear RX status alert bit.
> > +      * Clear overflow as well if set.
> > +      */
> > +     ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
> > +                             TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
> > +                             TCPC_ALERT_RX_STATUS);
> > +     if (ret < 0)
> > +             return;
> > +
> > +     tcpm_pd_receive(chip->port, &msg);
> > +}
> > +
> > +static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink)
> > +{
> > +     struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
> > +     u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE};
> > +     u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK};
> > +     u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF};
> > +     struct i2c_client *i2c = chip->client;
> > +     int ret;
> > +
> > +     struct i2c_msg msgs[] = {
> > +             {
> > +                     .addr = MAX_BUCK_BOOST_SID,
> > +                     .flags = i2c->flags & I2C_M_TEN,
> > +                     .len = 2,
> > +                     .buf = source ? buffer_source : sink ? buffer_sink : buffer_none,
> > +             },
> > +     };
> > +
> > +     if (source && sink) {
> > +             dev_err(chip->dev, "Both source and sink set\n");
> > +             return -EINVAL;
> > +     }
>
> So can buffer_none ever be used?

Yes. When TCPM makes the call here
https://elixir.bootlin.com/linux/latest/source/drivers/usb/typec/tcpm/tcpm.c#L2602.
@ tcpm_init_vbus which is called in the source/sink disconnect path.
Does that address your question ?


>
> > +     ret = i2c_transfer(i2c->adapter, msgs, 1);
> > +
> > +     return  ret < 0 ? ret : 1;
> > +}
> > +
> > +static void process_power_status(struct max_tcpci_chip *chip)
> > +{
> > +     u8 pwr_status;
> > +     int ret;
> > +
> > +     ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &pwr_status);
> > +     if (ret < 0)
> > +             return;
> > +
> > +     if (pwr_status == 0xff)
> > +             max_tcpci_init_regs(chip);
> > +     else
> > +             tcpm_vbus_change(chip->port);
> > +}
> > +
> > +static void process_tx(struct max_tcpci_chip *chip, u16 status)
> > +{
> > +     if (status & TCPC_ALERT_TX_SUCCESS)
> > +             tcpm_pd_transmit_complete(chip->port, TCPC_TX_SUCCESS);
> > +     else if (status & TCPC_ALERT_TX_DISCARDED)
> > +             tcpm_pd_transmit_complete(chip->port, TCPC_TX_DISCARDED);
> > +     else if (status & TCPC_ALERT_TX_FAILED)
> > +             tcpm_pd_transmit_complete(chip->port, TCPC_TX_FAILED);
> > +
> > +     /* Reinit regs as Hard reset sets them to default value */
> > +     if ((status & TCPC_ALERT_TX_SUCCESS) && (status & TCPC_ALERT_TX_FAILED))
> > +             max_tcpci_init_regs(chip);
> > +}
> > +
> > +static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
> > +{
> > +     u16 mask;
> > +     int ret;
> > +
> > +     /*
> > +      * Clear alert status for everything except RX_STATUS, which shouldn't
> > +      * be cleared until we have successfully retrieved message.
> > +      */
> > +     if (status & ~TCPC_ALERT_RX_STATUS) {
> > +             mask = status & TCPC_ALERT_RX_BUF_OVF ?
> > +                     status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
> > +                     status & ~TCPC_ALERT_RX_STATUS;
> > +             ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
> > +             if (ret < 0) {
> > +                     dev_err(chip->dev, "ALERT clear failed\n");
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     if (status & TCPC_ALERT_RX_BUF_OVF && !(status & TCPC_ALERT_RX_STATUS)) {
> > +             ret = max_tcpci_write16(chip, TCPC_ALERT, (TCPC_ALERT_RX_STATUS |
> > +                                                       TCPC_ALERT_RX_BUF_OVF));
> > +             if (ret < 0) {
> > +                     dev_err(chip->dev, "ALERT clear failed\n");
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     if (status & TCPC_ALERT_RX_STATUS)
> > +             process_rx(chip, status);
> > +
> > +     if (status & TCPC_ALERT_TX_DISCARDED)
> > +             dev_info(chip->dev, "TX_DISCARDED");
>
> What does that mean? Is it relevant for the user?
This indicates that most recent transmission of the pd packet is not successful.
I believe TCPM also prints this info in the TCPM debugfs logs
(status=2 if I remember right).
I will remove the log since it's redundant.

>
> > +
> > +     if (status & TCPC_ALERT_VBUS_DISCNCT)
> > +             tcpm_vbus_change(chip->port);
> > +
> > +     if (status & TCPC_ALERT_CC_STATUS)
> > +             tcpm_cc_change(chip->port);
> > +
> > +     if (status & TCPC_ALERT_POWER_STATUS)
> > +             process_power_status(chip);
> > +
> > +     if (status & TCPC_ALERT_RX_HARD_RST) {
> > +             tcpm_pd_hard_reset(chip->port);
> > +             max_tcpci_init_regs(chip);
> > +     }
> > +
> > +     if (status & TCPC_ALERT_TX_SUCCESS || status & TCPC_ALERT_TX_DISCARDED || status &
> > +         TCPC_ALERT_TX_FAILED)
> > +             process_tx(chip, status);
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
> > +{
> > +     struct max_tcpci_chip *chip = dev_id;
> > +     u16 status;
> > +     irqreturn_t irq_return;
> > +     int ret;
> > +
> > +     if (!chip->port)
> > +             return IRQ_HANDLED;
> > +
> > +     ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
> > +     if (ret < 0) {
> > +             dev_err(chip->dev, "ALERT read failed\n");
> > +             return ret;
> > +     }
> > +     while (status) {
> > +             irq_return = _max_tcpci_irq(chip, status);
> > +             /* Do not return if the ALERT is already set. */
> > +             ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
> > +             if (ret < 0)
> > +                     break;
> > +     }
> > +
> > +     return irq_return;
> > +}
> > +
> > +static irqreturn_t max_tcpci_isr(int irq, void *dev_id)
> > +{
> > +     struct max_tcpci_chip *chip = dev_id;
> > +
> > +     pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS);
> > +
> > +     if (!chip->port)
> > +             return IRQ_HANDLED;
> > +
> > +     return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client)
> > +{
> > +     int ret, irq_gpio;
> > +
> > +     irq_gpio = of_get_named_gpio(client->dev.of_node, "usbpd,usbpd_int", 0);
> > +     client->irq = gpio_to_irq(irq_gpio);
> > +     if (!client->irq)
> > +             return -ENODEV;
> > +
> > +     ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
> > +                                     (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
> > +                                     chip);
> > +
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     enable_irq_wake(client->irq);
> > +     return 0;
> > +}
> > +
> > +static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata,
> > +                                 enum typec_cc_status cc)
> > +{
> > +     struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
> > +
> > +     max_tcpci_init_regs(chip);
> > +
> > +     return 0;
> > +}
> > +
> > +static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
> > +{
> > +     /*
> > +      * Generic TCPCI overwrites the regs once this driver initializes
> > +      * them. Prevent this by returning -1.
> > +      */
> > +     return -1;
> > +}
> > +
> > +static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)
> > +{
> > +     int ret;
> > +     struct max_tcpci_chip *chip;
> > +     u8 power_status;
> > +
> > +     chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> > +     if (!chip)
> > +             return -ENOMEM;
> > +
> > +     chip->client = client;
> > +     chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
> > +     if (IS_ERR(chip->data.regmap)) {
> > +             dev_err(&client->dev, "Regmap init failed\n");
> > +             return PTR_ERR(chip->data.regmap);
> > +     }
> > +
> > +     chip->dev = &client->dev;
> > +     i2c_set_clientdata(client, chip);
> > +
> > +     ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     if (power_status & TCPC_POWER_STATUS_UNINIT) {
> > +             dev_err(&client->dev, "TCPC not ready!");
> > +             return -EPROBE_DEFER;
> > +     }
>
> That looks wrong. There is no guarantee that this wasn't the last
> device that is registered for a while. Or is there?
>
> I think you should consider TCPC_POWER_STATUS_UNINIT in tcpci_init(),
> just like tcpci.c does. Or is there some reason why you are checking
> it here?

There is no additional reason. I missed that tcpci_iinit code is also
checking for this.
Also, misunderstood that the bit is cleared only once after power on as I read
"1b: The TCPC is still performing internal initialization and the only registers
that are guaranteed to return the correct values are 00h...0Fh" but missed
"The TCPM shall check the state of the TCPC Initialization Status bit
when it starts or **resets.**"
which is mentioned above the register description.

Will Remove.

> > +
> > +     /* Chip level tcpci callbacks */
> > +     chip->data.set_vbus = max_tcpci_set_vbus;
> > +     chip->data.start_drp_toggling = max_tcpci_start_toggling;
> > +     chip->data.TX_BUF_BYTE_x_hidden = true;
> > +     chip->data.init = tcpci_init;
> > +
> > +     max_tcpci_init_regs(chip);
> > +     chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
> > +     if (IS_ERR_OR_NULL(chip->tcpci)) {
> > +             dev_err(&client->dev, "TCPCI port registration failed");
> > +             ret = PTR_ERR(chip->tcpci);
> > +             return PTR_ERR(chip->tcpci);
> > +     }
> > +     chip->port = tcpci_get_tcpm_port(chip->tcpci);
> > +     ret = max_tcpci_init_alert(chip, client);
> > +     if (ret < 0)
> > +             goto unreg_port;
> > +
> > +     device_init_wakeup(chip->dev, true);
> > +     return 0;
> > +
> > +unreg_port:
> > +     tcpci_unregister_port(chip->tcpci);
> > +
> > +     return ret;
> > +}
> > +
> > +static int max_tcpci_remove(struct i2c_client *client)
> > +{
> > +     struct max_tcpci_chip *chip = i2c_get_clientdata(client);
> > +
> > +     if (!IS_ERR_OR_NULL(chip->tcpci))
> > +             tcpci_unregister_port(chip->tcpci);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct i2c_device_id max_tcpci_id[] = {
> > +     { "maxtcpc", 0 },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
> > +
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id max_tcpci_of_match[] = {
> > +     { .compatible = "maxim,tcpc", },
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, max_tcpci_of_match);
> > +#endif
> > +
> > +static struct i2c_driver max_tcpci_i2c_driver = {
> > +     .driver = {
> > +             .name = "maxtcpc",
> > +             .of_match_table = of_match_ptr(max_tcpci_of_match),
> > +     },
> > +     .probe = max_tcpci_probe,
> > +     .remove = max_tcpci_remove,
> > +     .id_table = max_tcpci_id,
> > +};
> > +module_i2c_driver(max_tcpci_i2c_driver);
> > +
> > +MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
> > +MODULE_DESCRIPTION("Maxim TCPCI based USB Type-C Port Controller Interface Driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.28.0.402.g5ffc5be6b7-goog
>
> thanks,
>
> --
> heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect
  2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
                   ` (15 preceding siblings ...)
  2020-09-15 13:33 ` Heikki Krogerus
@ 2020-09-16 11:05 ` Greg Kroah-Hartman
  16 siblings, 0 replies; 32+ messages in thread
From: Greg Kroah-Hartman @ 2020-09-16 11:05 UTC (permalink / raw)
  To: Badhri Jagan Sridharan
  Cc: Guenter Roeck, Heikki Krogerus, linux-usb, linux-kernel

On Mon, Aug 31, 2020 at 07:59:13PM -0700, Badhri Jagan Sridharan wrote:
> First of all apologies for mixing up the patch version as noted by
> Heikki and Greg. All of them were v1's but since I was manually adding
> the version numbers I mixed them up. Using the --reroll-count option
> now. Updating the patch version to v6 (highest version number in the
> previous patchset + 1) to avoid confusion.
> 
> I also rebased on to off of the recent usb-next tip:
> 5fedf0d295d3 (origin/usb-testing, origin/usb-next) Merge 5.9-rc3 into usb-next
> Which had the following changes causing merge conflict:
> 3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> 6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> 
> Addressed comments from Heikki and Randy which have described in the
> individual commit's change history as well.

First 3 patches now queued up, thanks.

greg k-h

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS)
  2020-09-15 13:20   ` Heikki Krogerus
@ 2020-09-17 10:23     ` Badhri Jagan Sridharan
  0 siblings, 0 replies; 32+ messages in thread
From: Badhri Jagan Sridharan @ 2020-09-17 10:23 UTC (permalink / raw)
  To: Heikki Krogerus; +Cc: Guenter Roeck, Greg Kroah-Hartman, USB, LKML

On Tue, Sep 15, 2020 at 6:20 AM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> On Mon, Aug 31, 2020 at 07:59:22PM -0700, Badhri Jagan Sridharan wrote:
> > PD 3.0 spec defines a new mechanism for power role swap called
> > Fast role swap. This change enables TCPM to support FRS when
> > acting as sink.
> >
> > Once the explicit contract is negotiated, sink port is
> > expected to query the source port for sink caps to
> > determine whether the source is FRS capable.
> > Bits 23 & 24 of fixed pdo of the sink caps from the source, when
> > set, indicates the current needed by the source when fast role
> > swap is in progress(Implicit contract phasae). 0 indicates that
> > the source does not support Fast Role Swap.
> >
> > Upon receiving the FRS signal from the source,
> > TCPC(TCPM_FRS_EVENT) informs TCPM to start the Fast role swap sequence.
> >
> > 1. TCPM sends FRS PD message: FR_SWAP_SEND
> > 2. If response is not received within the expiry of
> >    SenderResponseTimer, Error recovery is triggered.:
> >    FR_SWAP_SEND_TIMEOUT
> > 3. Upon receipt of the accept message, TCPM waits for
> >    PSSourceOffTimer for PS_READY message from the partner:
> >    FR_SWAP_SNK_SRC_NEW_SINK_READY.
> >
> > TCPC is expected to autonomously turn on vbus once the FRS
> > signal is received and vbus voltage falls below vsafe5v within
> > tSrcFrSwap. This is different from traditional power role swap
> > where the vbus sourcing is turned on by TCPM.
> >
> > 4. By this time, TCPC most likely would have started to
> >    source vbus, TCPM waits for tSrcFrSwap to see  if the
> >    lower level TCPC driver signals TCPM_SOURCING_VBUS event:
> >    FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED.
> > 5. When TCPC signals sourcing vbus, TCPM sends PS_READY msg and
> >    changes the CC pin from Rd to Rp. This is the end of fast
> >    role swap sequence and TCPM initiates the sequnce to negotiate
> >    explicit contract by transitioning into SRC_STARTUP after
> >    SwapSrcStart.
> >
> > The code is written based on the sequence described in "Figure 8-107:
> > Dual-role Port in Sink to Source Fast Role Swap State Diagram" of
> > USB Power Delivery Specification Revision 3.0, Version 1.2.
> >
> > Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
> > ---
> > Changes since v1:
> > - Changing patch version to v6 to fix version number confusion.
> > - Rebased on top of usb-next and resolved conflicts due to the below
> >   changes:
> >   3ed8e1c2ac99 usb: typec: tcpm: Migrate workqueue to RT priority for processing events
> >   6bbe2a90a0bb usb: typec: tcpm: During PR_SWAP, source caps should be sent only after tSwapSourceStart
> > - enable_frs sequence is now run as part of the same kthread that runs
> >   the state machines.
> > - Fixed the implicit fallthrough warning in the switch case for
> >   FR_SWAP_CANCEL case.
> > ---
> >  drivers/usb/typec/tcpm/tcpm.c | 217 +++++++++++++++++++++++++++++++++-
> >  include/linux/usb/pd.h        |  19 +--
> >  include/linux/usb/tcpm.h      |   8 +-
> >  include/linux/usb/typec.h     |  13 ++
> >  4 files changed, 245 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> > index 92806547f485..083e7af107b2 100644
> > --- a/drivers/usb/typec/tcpm/tcpm.c
> > +++ b/drivers/usb/typec/tcpm/tcpm.c
> > @@ -106,6 +106,13 @@
> >       S(VCONN_SWAP_TURN_ON_VCONN),            \
> >       S(VCONN_SWAP_TURN_OFF_VCONN),           \
> >                                               \
> > +     S(FR_SWAP_SEND),                        \
> > +     S(FR_SWAP_SEND_TIMEOUT),                \
> > +     S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF),                   \
> > +     S(FR_SWAP_SNK_SRC_NEW_SINK_READY),              \
> > +     S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED), \
> > +     S(FR_SWAP_CANCEL),                      \
> > +                                             \
> >       S(SNK_TRY),                             \
> >       S(SNK_TRY_WAIT),                        \
> >       S(SNK_TRY_WAIT_DEBOUNCE),               \
> > @@ -127,6 +134,9 @@
> >       S(GET_PPS_STATUS_SEND),                 \
> >       S(GET_PPS_STATUS_SEND_TIMEOUT),         \
> >                                               \
> > +     S(GET_SINK_CAP),                        \
> > +     S(GET_SINK_CAP_TIMEOUT),                \
> > +                                             \
> >       S(ERROR_RECOVERY),                      \
> >       S(PORT_RESET),                          \
> >       S(PORT_RESET_WAIT_OFF)
> > @@ -175,6 +185,8 @@ enum adev_actions {
> >  #define TCPM_CC_EVENT                BIT(0)
> >  #define TCPM_VBUS_EVENT              BIT(1)
> >  #define TCPM_RESET_EVENT     BIT(2)
> > +#define TCPM_FRS_EVENT               BIT(3)
> > +#define TCPM_SOURCING_VBUS   BIT(4)
> >
> >  #define LOG_BUFFER_ENTRIES   1024
> >  #define LOG_BUFFER_ENTRY_SIZE        128
> > @@ -184,6 +196,8 @@ enum adev_actions {
> >  #define SVID_DISCOVERY_MAX   16
> >  #define ALTMODE_DISCOVERY_MAX        (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
> >
> > +#define GET_SINK_CAP_RETRY_MS        100
> > +
> >  struct pd_mode_data {
> >       int svid_index;         /* current SVID index           */
> >       int nsvids;
> > @@ -261,6 +275,8 @@ struct tcpm_port {
> >       struct kthread_work state_machine;
> >       struct hrtimer vdm_state_machine_timer;
> >       struct kthread_work vdm_state_machine;
> > +     struct hrtimer enable_frs_timer;
> > +     struct kthread_work enable_frs;
> >       bool state_machine_running;
> >
> >       struct completion tx_complete;
> > @@ -335,6 +351,12 @@ struct tcpm_port {
> >       /* port belongs to a self powered device */
> >       bool self_powered;
> >
> > +     /* FRS */
> > +     enum frs_typec_current frs_current;
>
> Do you use this member anywhere?
>
> I can't see anywhere it being used, but I think you should use it
> instead the one in port_caps.
Yes I started with having it here and then switched to port_caps
while forgetting this !
Have gone back to using the one here.

>
> > +     /* Sink caps have been queried */
> > +     bool sink_cap_done;
> > +
> >  #ifdef CONFIG_DEBUG_FS
> >       struct dentry *dentry;
> >       struct mutex logbuffer_lock;    /* log buffer access lock */
> > @@ -940,6 +962,16 @@ static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
> >       }
> >  }
>
> <snip>
>
> > diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> > index 9cb1bec94b71..2ec7451a14ab 100644
> > --- a/include/linux/usb/typec.h
> > +++ b/include/linux/usb/typec.h
> > @@ -204,6 +204,18 @@ struct typec_operations {
> >                            enum typec_port_type type);
> >  };
> >
> > +/*
> > + * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
> > + * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
> > + * Version 1.2"
> > + */
> > +enum frs_typec_current {
> > +     FRS_NOT_SUPPORTED,
> > +     FRS_DEFAULT_POWER,
> > +     FRS_5V_1P5A,
> > +     FRS_5V_3A,
> > +};
> > +
> >  /*
> >   * struct typec_capability - USB Type-C Port Capabilities
> >   * @type: Supported power role of the port
> > @@ -226,6 +238,7 @@ struct typec_capability {
> >       int                     prefer_role;
> >       enum typec_accessory    accessory[TYPEC_MAX_ACCESSORY];
> >       unsigned int            orientation_aware:1;
> > +     enum frs_typec_current  frs_current;
>
> So forget about this. We can add it here if/when there is another
> driver that needs that. You already have that member in struct
> tcpm_port, so just use that for now.
>
> And you can also introduce enum frs_typec_current in tcpm.c for now.

Sure ! Done in v7.

>
> >       struct fwnode_handle    *fwnode;
> >       void                    *driver_data;
> > --
> > 2.28.0.402.g5ffc5be6b7-goog
>
> thanks,
>
> --
> heikki

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2020-09-17 10:24 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-01  2:59 [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 01/14] usb: typec: tcpci: Add register definitions to tcpci Badhri Jagan Sridharan
2020-09-15 12:10   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 02/14] usb: typec: tcpci: Add support when hidden tx registers are inaccessible Badhri Jagan Sridharan
2020-09-15 12:11   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 03/14] usb: typec: tcpci: update ROLE_CONTROL for DRP Badhri Jagan Sridharan
2020-09-15 12:12   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 04/14] usb: typec: tcpci: Add a getter method to retrieve tcpm_port reference Badhri Jagan Sridharan
2020-09-15 12:16   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 05/14] usb: typec: tcpci: Add set_vbus tcpci callback Badhri Jagan Sridharan
2020-09-15 12:22   ` Heikki Krogerus
2020-09-16  9:07     ` Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 06/14] dt-bindings: usb: Maxim type-c controller device tree binding document Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 07/14] usb: typec: tcpci_maxim: Chip level TCPC driver Badhri Jagan Sridharan
2020-09-15 12:43   ` Heikki Krogerus
2020-09-16  9:34     ` Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 08/14] dt-bindings: connector: Add property to set initial current cap for FRS Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 09/14] usb: typec: tcpm: Add support for Sink Fast Role SWAP(FRS) Badhri Jagan Sridharan
2020-09-15 13:20   ` Heikki Krogerus
2020-09-17 10:23     ` Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 10/14] usb: typec: tcpci: Implement callbacks for FRS Badhri Jagan Sridharan
2020-09-15 13:23   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 11/14] usb: typec: tcpci_maxim: Add support for Sink FRS Badhri Jagan Sridharan
2020-09-15 13:25   ` Heikki Krogerus
2020-09-01  2:59 ` [PATCH v6 12/14] usb: typec: tcpm: Implement enabling Auto Discharge disconnect support Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 13/14] usb: typec: tcpci: Implement Auto discharge disconnect callbacks Badhri Jagan Sridharan
2020-09-01  2:59 ` [PATCH v6 14/14] usb: typec: tcpci_maxim: Implemnent set_auto_vbus_discharge_threshold Badhri Jagan Sridharan
2020-09-15 12:09 ` [PATCH v6 00/14] TCPM support for FRS and AutoDischarge Disconnect Heikki Krogerus
2020-09-15 16:57   ` Badhri Jagan Sridharan
2020-09-15 13:33 ` Heikki Krogerus
2020-09-15 16:59   ` Badhri Jagan Sridharan
2020-09-16 11:05 ` Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).