linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] dt-bindings: vendor-prefixes: Add willsemi
@ 2022-02-01  3:24 Samuel Holland
  2022-02-01  3:24 ` [PATCH 2/4] dt-bindings: usb: Add WUSB3801 Type-C Port Controller Samuel Holland
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Samuel Holland @ 2022-02-01  3:24 UTC (permalink / raw)
  To: Heikki Krogerus, Guenter Roeck, Greg Kroah-Hartman, Rob Herring,
	devicetree
  Cc: linux-usb, linux-kernel, Samuel Holland

Add prefix for Will Semiconductor Co. Ltd. (http://www.willsemi.com/)

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 294093d45a23..a8ab97717a46 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1340,6 +1340,8 @@ patternProperties:
     description: Wi2Wi, Inc.
   "^wiligear,.*":
     description: Wiligear, Ltd.
+  "^willsemi,.*":
+    description: Will Semiconductor Ltd.
   "^winbond,.*":
     description: Winbond Electronics corp.
   "^wingtech,.*":
-- 
2.33.1


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

* [PATCH 2/4] dt-bindings: usb: Add WUSB3801 Type-C Port Controller
  2022-02-01  3:24 [PATCH 1/4] dt-bindings: vendor-prefixes: Add willsemi Samuel Holland
@ 2022-02-01  3:24 ` Samuel Holland
  2022-02-01  3:24 ` [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties Samuel Holland
  2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
  2 siblings, 0 replies; 9+ messages in thread
From: Samuel Holland @ 2022-02-01  3:24 UTC (permalink / raw)
  To: Heikki Krogerus, Guenter Roeck, Greg Kroah-Hartman, Rob Herring,
	devicetree
  Cc: linux-usb, linux-kernel, Samuel Holland

Add devicetree support for the Will Semiconductor WUSB3801. This is a
basic non-PD Type-C port controller.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 .../bindings/usb/willsemi,wusb3801.yaml       | 75 +++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/willsemi,wusb3801.yaml

diff --git a/Documentation/devicetree/bindings/usb/willsemi,wusb3801.yaml b/Documentation/devicetree/bindings/usb/willsemi,wusb3801.yaml
new file mode 100644
index 000000000000..c2b2243c7892
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/willsemi,wusb3801.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/willsemi,wusb3801.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WUSB3801 Type-C port controller DT bindings
+
+description:
+  The Will Semiconductor WUSB3801 is a USB Type-C port controller which
+  supports role and plug orientation detection using the CC pins. It is
+  compatible with the USB Type-C Cable and Connector Specification v1.2.
+
+maintainers:
+  - Samuel Holland <samuel@sholland.org>
+
+properties:
+  compatible:
+    enum:
+      - willsemi,wusb3801
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  connector:
+    type: object
+    $ref: ../connector/usb-connector.yaml#
+    description:
+      The managed USB Type-C connector. Since WUSB3801 does not support
+      Power Delivery, the node should have the "pd-disable" property.
+
+    properties:
+      compatible:
+        const: usb-c-connector
+
+    required:
+      - pd-disable
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - connector
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      tcpc@60 {
+        compatible = "willsemi,wusb3801";
+        reg = <0x60>;
+        interrupt-parent = <&gpio0>;
+        interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+
+        connector {
+          compatible = "usb-c-connector";
+          label = "USB-C";
+          vbus-supply = <&otg_switch>;
+          power-role = "dual";
+          try-power-role = "sink";
+          data-role = "dual";
+          typec-power-opmode = "default";
+          pd-disable;
+        };
+      };
+    };
-- 
2.33.1


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

* [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties
  2022-02-01  3:24 [PATCH 1/4] dt-bindings: vendor-prefixes: Add willsemi Samuel Holland
  2022-02-01  3:24 ` [PATCH 2/4] dt-bindings: usb: Add WUSB3801 Type-C Port Controller Samuel Holland
@ 2022-02-01  3:24 ` Samuel Holland
  2022-02-02  6:22   ` kernel test robot
  2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
  2 siblings, 1 reply; 9+ messages in thread
From: Samuel Holland @ 2022-02-01  3:24 UTC (permalink / raw)
  To: Heikki Krogerus, Guenter Roeck, Greg Kroah-Hartman, Rob Herring,
	devicetree
  Cc: linux-usb, linux-kernel, Samuel Holland

Basic programmable non-PD Type-C port controllers do not need the full
TCPM library, but they share the same devicetree binding and the same
typec_capability structure. Factor out a helper for parsing those
properties which map to fields in struct typec_capability, so the code
can be shared between TCPM and basic non-TCPM drivers.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 drivers/usb/typec/class.c     | 49 +++++++++++++++++++++++++++++++++++
 drivers/usb/typec/tcpm/tcpm.c | 32 +----------------------
 include/linux/usb/typec.h     |  3 +++
 3 files changed, 53 insertions(+), 31 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45a6f0c807cb..8863d2e93d50 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1894,6 +1894,55 @@ void *typec_get_drvdata(struct typec_port *port)
 }
 EXPORT_SYMBOL_GPL(typec_get_drvdata);
 
+int typec_get_fw_cap(struct typec_capability *cap,
+		     struct fwnode_handle *fwnode)
+{
+	const char *cap_str;
+	int ret;
+
+	/*
+	 * This fwnode has a "compatible" property, but is never populated as a
+	 * struct device. Instead we simply parse it to read the properties.
+	 * This it breaks fw_devlink=on. To maintain backward compatibility
+	 * with existing DT files, we work around this by deleting any
+	 * fwnode_links to/from this fwnode.
+	 */
+	fw_devlink_purge_absent_suppliers(fwnode);
+
+	cap->fwnode = fwnode;
+
+	ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+	if (ret < 0)
+		return ret;
+
+	cap->type = typec_find_port_power_role(cap_str);
+	if (cap->type < 0)
+		return cap->type;
+
+	/* USB data support is optional */
+	ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
+	if (ret == 0) {
+		cap->data = typec_find_port_data_role(cap_str);
+		if (cap->data < 0)
+			return cap->data;
+	}
+
+	/* Get the preferred power role for a DRP */
+	if (cap->type == TYPEC_PORT_DRP) {
+		cap->prefer_role = TYPEC_NO_PREFERRED_ROLE;
+
+		ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str);
+		if (ret == 0) {
+			cap->prefer_role = typec_find_power_role(cap_str);
+			if (cap->prefer_role < 0)
+				return cap->prefer_role;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_get_fw_cap);
+
 /**
  * typec_port_register_altmode - Register USB Type-C Port Alternate Mode
  * @port: USB Type-C Port that supports the alternate mode
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 5fce795b69c7..8b58aa6e3509 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -5935,32 +5935,10 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
 	if (!fwnode)
 		return -EINVAL;
 
-	/*
-	 * This fwnode has a "compatible" property, but is never populated as a
-	 * struct device. Instead we simply parse it to read the properties.
-	 * This it breaks fw_devlink=on. To maintain backward compatibility
-	 * with existing DT files, we work around this by deleting any
-	 * fwnode_links to/from this fwnode.
-	 */
-	fw_devlink_purge_absent_suppliers(fwnode);
-
-	/* USB data support is optional */
-	ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
-	if (ret == 0) {
-		ret = typec_find_port_data_role(cap_str);
-		if (ret < 0)
-			return ret;
-		port->typec_caps.data = ret;
-	}
-
-	ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+	ret = typec_get_fw_cap(&port->typec_caps, fwnode);
 	if (ret < 0)
 		return ret;
 
-	ret = typec_find_port_power_role(cap_str);
-	if (ret < 0)
-		return ret;
-	port->typec_caps.type = ret;
 	port->port_type = port->typec_caps.type;
 	port->pd_supported = !fwnode_property_read_bool(fwnode, "pd-disable");
 
@@ -5997,14 +5975,6 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
 	if (port->port_type == TYPEC_PORT_SRC)
 		return 0;
 
-	/* Get the preferred power role for DRP */
-	ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str);
-	if (ret < 0)
-		return ret;
-
-	port->typec_caps.prefer_role = typec_find_power_role(cap_str);
-	if (port->typec_caps.prefer_role < 0)
-		return -EINVAL;
 sink:
 	port->self_powered = fwnode_property_read_bool(fwnode, "self-powered");
 
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 7ba45a97eeae..fdf737d48b3b 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -295,6 +295,9 @@ int typec_set_mode(struct typec_port *port, int mode);
 
 void *typec_get_drvdata(struct typec_port *port);
 
+int typec_get_fw_cap(struct typec_capability *cap,
+		     struct fwnode_handle *fwnode);
+
 int typec_find_pwr_opmode(const char *name);
 int typec_find_orientation(const char *name);
 int typec_find_port_power_role(const char *name);
-- 
2.33.1


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

* [PATCH 4/4] usb: typec: Support the WUSB3801 port controller
  2022-02-01  3:24 [PATCH 1/4] dt-bindings: vendor-prefixes: Add willsemi Samuel Holland
  2022-02-01  3:24 ` [PATCH 2/4] dt-bindings: usb: Add WUSB3801 Type-C Port Controller Samuel Holland
  2022-02-01  3:24 ` [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties Samuel Holland
@ 2022-02-01  3:24 ` Samuel Holland
  2022-02-01 10:28   ` Greg Kroah-Hartman
                     ` (2 more replies)
  2 siblings, 3 replies; 9+ messages in thread
From: Samuel Holland @ 2022-02-01  3:24 UTC (permalink / raw)
  To: Heikki Krogerus, Guenter Roeck, Greg Kroah-Hartman, Rob Herring,
	devicetree
  Cc: linux-usb, linux-kernel, Samuel Holland

WUSB3801 features a configurable port type, accessory detection, and
plug orientation detection. It provides a hardware "ID" pin output for
compatibility with USB 2.0 OTG PHYs. Add a typec class driver for it.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

 drivers/usb/typec/Kconfig    |  10 +
 drivers/usb/typec/Makefile   |   1 +
 drivers/usb/typec/wusb3801.c | 445 +++++++++++++++++++++++++++++++++++
 3 files changed, 456 insertions(+)
 create mode 100644 drivers/usb/typec/wusb3801.c

diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index ab480f38523a..74acde9fc09f 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -88,6 +88,16 @@ config TYPEC_QCOM_PMIC
 	  It will also enable the VBUS output to connected devices when a
 	  DFP connection is made.
 
+config TYPEC_WUSB3801
+	tristate "Willsemi WUSB3801 Type-C port controller driver"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y or M here if your system has a WUSB3801 Type-C port controller.
+
+	  If you choose to build this driver as a dynamically linked module, the
+	  module will be called wusb3801.ko.
+
 source "drivers/usb/typec/mux/Kconfig"
 
 source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 57870a2bd787..3b722ec28d99 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_TYPEC_TPS6598X)	+= tipd/
 obj-$(CONFIG_TYPEC_HD3SS3220)	+= hd3ss3220.o
 obj-$(CONFIG_TYPEC_QCOM_PMIC)	+= qcom-pmic-typec.o
 obj-$(CONFIG_TYPEC_STUSB160X) 	+= stusb160x.o
+obj-$(CONFIG_TYPEC_WUSB3801)	+= wusb3801.o
 obj-$(CONFIG_TYPEC)		+= mux/
diff --git a/drivers/usb/typec/wusb3801.c b/drivers/usb/typec/wusb3801.c
new file mode 100644
index 000000000000..e4ff37bcce9c
--- /dev/null
+++ b/drivers/usb/typec/wusb3801.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Willsemi WUSB3801 Type-C port controller driver
+ *
+ * Copyright (C) 2022 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec.h>
+
+#define WUSB3801_REG_DEVICE_ID		0x01
+#define WUSB3801_REG_CTRL0		0x02
+#define WUSB3801_REG_INT		0x03
+#define WUSB3801_REG_STAT		0x04
+#define WUSB3801_REG_CTRL1		0x05
+#define WUSB3801_REG_TEST00		0x06
+#define WUSB3801_REG_TEST01		0x07
+#define WUSB3801_REG_TEST02		0x08
+#define WUSB3801_REG_TEST03		0x09
+#define WUSB3801_REG_TEST04		0x0a
+#define WUSB3801_REG_TEST05		0x0b
+#define WUSB3801_REG_TEST06		0x0c
+#define WUSB3801_REG_TEST07		0x0d
+#define WUSB3801_REG_TEST08		0x0e
+#define WUSB3801_REG_TEST09		0x0f
+#define WUSB3801_REG_TEST0A		0x10
+#define WUSB3801_REG_TEST0B		0x11
+#define WUSB3801_REG_TEST0C		0x12
+#define WUSB3801_REG_TEST0D		0x13
+#define WUSB3801_REG_TEST0E		0x14
+#define WUSB3801_REG_TEST0F		0x15
+#define WUSB3801_REG_TEST10		0x16
+#define WUSB3801_REG_TEST11		0x17
+#define WUSB3801_REG_TEST12		0x18
+
+#define WUSB3801_DEVICE_ID_VERSION_ID	GENMASK(7, 3)
+#define WUSB3801_DEVICE_ID_VENDOR_ID	GENMASK(2, 0)
+
+#define WUSB3801_CTRL0_DIS_ACC_SUPPORT	BIT(7)
+#define WUSB3801_CTRL0_TRY		GENMASK(6, 5)
+#define WUSB3801_CTRL0_TRY_NONE		(0x0 << 5)
+#define WUSB3801_CTRL0_TRY_SNK		(0x1 << 5)
+#define WUSB3801_CTRL0_TRY_SRC		(0x2 << 5)
+#define WUSB3801_CTRL0_CURRENT		GENMASK(4, 3) /* SRC */
+#define WUSB3801_CTRL0_CURRENT_DEFAULT	(0x0 << 3)
+#define WUSB3801_CTRL0_CURRENT_1_5A	(0x1 << 3)
+#define WUSB3801_CTRL0_CURRENT_3_0A	(0x2 << 3)
+#define WUSB3801_CTRL0_ROLE		GENMASK(2, 1)
+#define WUSB3801_CTRL0_ROLE_SNK		(0x0 << 1)
+#define WUSB3801_CTRL0_ROLE_SRC		(0x1 << 1)
+#define WUSB3801_CTRL0_ROLE_DRP		(0x2 << 1)
+#define WUSB3801_CTRL0_INT_MASK		BIT(0)
+
+#define WUSB3801_INT_ATTACHED		BIT(0)
+#define WUSB3801_INT_DETACHED		BIT(1)
+
+#define WUSB3801_STAT_VBUS_DETECTED	BIT(7)
+#define WUSB3801_STAT_CURRENT		GENMASK(6, 5) /* SNK */
+#define WUSB3801_STAT_CURRENT_STANDBY	(0x0 << 5)
+#define WUSB3801_STAT_CURRENT_DEFAULT	(0x1 << 5)
+#define WUSB3801_STAT_CURRENT_1_5A	(0x2 << 5)
+#define WUSB3801_STAT_CURRENT_3_0A	(0x3 << 5)
+#define WUSB3801_STAT_PARTNER		GENMASK(4, 2)
+#define WUSB3801_STAT_PARTNER_STANDBY	(0x0 << 2)
+#define WUSB3801_STAT_PARTNER_SNK	(0x1 << 2)
+#define WUSB3801_STAT_PARTNER_SRC	(0x2 << 2)
+#define WUSB3801_STAT_PARTNER_AUDIO	(0x3 << 2)
+#define WUSB3801_STAT_PARTNER_DEBUG	(0x4 << 2)
+#define WUSB3801_STAT_ORIENTATION	GENMASK(1, 0)
+#define WUSB3801_STAT_ORIENTATION_NONE	(0x0 << 0)
+#define WUSB3801_STAT_ORIENTATION_CC1	(0x1 << 0)
+#define WUSB3801_STAT_ORIENTATION_CC2	(0x2 << 0)
+#define WUSB3801_STAT_ORIENTATION_BOTH	(0x3 << 0)
+
+#define WUSB3801_CTRL1_SM_RESET		BIT(0)
+
+#define WUSB3801_TEST01_VENDOR_SUB_ID	(BIT(8) | BIT(6))
+
+#define WUSB3801_TEST02_FORCE_ERR_RCY	BIT(8)
+
+#define WUSB3801_TEST0A_WAIT_VBUS	BIT(5)
+
+struct wusb3801 {
+	struct typec_capability	cap;
+	struct device		*dev;
+	struct typec_partner	*partner;
+	struct typec_port	*port;
+	struct regmap		*regmap;
+	struct regulator	*vbus_supply;
+	unsigned int		partner_type;
+	enum typec_port_type	port_type;
+	enum typec_pwr_opmode	pwr_opmode;
+	bool			vbus_on;
+};
+
+static enum typec_role wusb3801_get_default_role(struct wusb3801 *wusb3801)
+{
+	switch (wusb3801->port_type) {
+	case TYPEC_PORT_SRC:
+		return TYPEC_SOURCE;
+	case TYPEC_PORT_SNK:
+		return TYPEC_SINK;
+	case TYPEC_PORT_DRP:
+	default:
+		if (wusb3801->cap.prefer_role == TYPEC_SOURCE)
+			return TYPEC_SOURCE;
+		return TYPEC_SINK;
+	}
+}
+
+static int wusb3801_map_port_type(enum typec_port_type type)
+{
+	switch (type) {
+	case TYPEC_PORT_SRC:
+		return WUSB3801_CTRL0_ROLE_SRC;
+	case TYPEC_PORT_SNK:
+		return WUSB3801_CTRL0_ROLE_SNK;
+	case TYPEC_PORT_DRP:
+	default:
+		return WUSB3801_CTRL0_ROLE_DRP;
+	}
+}
+
+static int wusb3801_map_pwr_opmode(enum typec_pwr_opmode mode)
+{
+	switch (mode) {
+	case TYPEC_PWR_MODE_USB:
+	default:
+		return WUSB3801_CTRL0_CURRENT_DEFAULT;
+	case TYPEC_PWR_MODE_1_5A:
+		return WUSB3801_CTRL0_CURRENT_1_5A;
+	case TYPEC_PWR_MODE_3_0A:
+		return WUSB3801_CTRL0_CURRENT_3_0A;
+	}
+}
+
+static unsigned int wusb3801_map_try_role(int role)
+{
+	switch (role) {
+	case TYPEC_NO_PREFERRED_ROLE:
+	default:
+		return WUSB3801_CTRL0_TRY_NONE;
+	case TYPEC_SINK:
+		return WUSB3801_CTRL0_TRY_SNK;
+	case TYPEC_SOURCE:
+		return WUSB3801_CTRL0_TRY_SRC;
+	}
+}
+
+static enum typec_orientation wusb3801_unmap_orientation(unsigned int status)
+{
+	switch (status & WUSB3801_STAT_ORIENTATION) {
+	case WUSB3801_STAT_ORIENTATION_NONE:
+	case WUSB3801_STAT_ORIENTATION_BOTH:
+		return TYPEC_ORIENTATION_NONE;
+	case WUSB3801_STAT_ORIENTATION_CC1:
+		return TYPEC_ORIENTATION_NORMAL;
+	case WUSB3801_STAT_ORIENTATION_CC2:
+		return TYPEC_ORIENTATION_REVERSE;
+	}
+
+	unreachable();
+}
+
+static enum typec_pwr_opmode wusb3801_unmap_pwr_opmode(unsigned int status)
+{
+	switch (status & WUSB3801_STAT_CURRENT) {
+	case WUSB3801_STAT_CURRENT_STANDBY:
+	case WUSB3801_STAT_CURRENT_DEFAULT:
+		return TYPEC_PWR_MODE_USB;
+	case WUSB3801_STAT_CURRENT_1_5A:
+		return TYPEC_PWR_MODE_1_5A;
+	case WUSB3801_STAT_CURRENT_3_0A:
+		return TYPEC_PWR_MODE_3_0A;
+	}
+
+	unreachable();
+}
+
+static int wusb3801_try_role(struct typec_port *port, int role)
+{
+	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
+
+	return regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
+				  WUSB3801_CTRL0_TRY,
+				  wusb3801_map_try_role(role));
+}
+
+static int wusb3801_port_type_set(struct typec_port *port,
+				  enum typec_port_type type)
+{
+	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
+	int ret;
+
+	ret = regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
+				 WUSB3801_CTRL0_ROLE,
+				 wusb3801_map_port_type(type));
+	if (ret)
+		return ret;
+
+	wusb3801->port_type = type;
+
+	return 0;
+}
+
+static const struct typec_operations wusb3801_typec_ops = {
+	.try_role	= wusb3801_try_role,
+	.port_type_set	= wusb3801_port_type_set,
+};
+
+static int wusb3801_hw_init(struct wusb3801 *wusb3801)
+{
+	return regmap_write(wusb3801->regmap, WUSB3801_REG_CTRL0,
+			    wusb3801_map_try_role(wusb3801->cap.prefer_role) |
+			    wusb3801_map_pwr_opmode(wusb3801->pwr_opmode) |
+			    wusb3801_map_port_type(wusb3801->port_type));
+}
+
+static void wusb3801_hw_update(struct wusb3801 *wusb3801)
+{
+	struct typec_port *port = wusb3801->port;
+	struct device *dev = wusb3801->dev;
+	unsigned int partner_type, status;
+	int ret;
+
+	ret = regmap_read(wusb3801->regmap, WUSB3801_REG_STAT, &status);
+	if (ret) {
+		dev_warn(dev, "Failed to read port status: %d\n", ret);
+		status = 0;
+	}
+	dev_dbg(dev, "status = 0x%02x\n", status);
+
+	partner_type = status & WUSB3801_STAT_PARTNER;
+
+	if (partner_type == WUSB3801_STAT_PARTNER_SNK) {
+		if (!wusb3801->vbus_on) {
+			ret = regulator_enable(wusb3801->vbus_supply);
+			if (ret)
+				dev_warn(dev, "Failed to enable VBUS: %d\n", ret);
+			wusb3801->vbus_on = true;
+		}
+	} else {
+		if (wusb3801->vbus_on) {
+			regulator_disable(wusb3801->vbus_supply);
+			wusb3801->vbus_on = false;
+		}
+	}
+
+	if (partner_type != wusb3801->partner_type) {
+		struct typec_partner_desc desc = {};
+		enum typec_data_role data_role;
+		enum typec_role pwr_role = wusb3801_get_default_role(wusb3801);
+
+		switch (partner_type) {
+		case WUSB3801_STAT_PARTNER_STANDBY:
+			break;
+		case WUSB3801_STAT_PARTNER_SNK:
+			pwr_role = TYPEC_SOURCE;
+			break;
+		case WUSB3801_STAT_PARTNER_SRC:
+			pwr_role = TYPEC_SINK;
+			break;
+		case WUSB3801_STAT_PARTNER_AUDIO:
+			desc.accessory = TYPEC_ACCESSORY_AUDIO;
+			break;
+		case WUSB3801_STAT_PARTNER_DEBUG:
+			desc.accessory = TYPEC_ACCESSORY_DEBUG;
+			break;
+		}
+
+		if (wusb3801->partner) {
+			typec_unregister_partner(wusb3801->partner);
+			wusb3801->partner = NULL;
+		}
+
+		if (partner_type != WUSB3801_STAT_PARTNER_STANDBY) {
+			wusb3801->partner = typec_register_partner(port, &desc);
+			if (IS_ERR(wusb3801->partner))
+				dev_err(dev, "Failed to register partner: %ld\n",
+					PTR_ERR(wusb3801->partner));
+		}
+
+		data_role = pwr_role == TYPEC_SOURCE ? TYPEC_HOST : TYPEC_DEVICE;
+		typec_set_data_role(port, data_role);
+		typec_set_pwr_role(port, pwr_role);
+		typec_set_vconn_role(port, pwr_role);
+	}
+
+	typec_set_pwr_opmode(wusb3801->port,
+			     partner_type == WUSB3801_STAT_PARTNER_SRC
+				? wusb3801_unmap_pwr_opmode(status)
+				: wusb3801->pwr_opmode);
+	typec_set_orientation(wusb3801->port,
+			      wusb3801_unmap_orientation(status));
+
+	wusb3801->partner_type = partner_type;
+}
+
+static irqreturn_t wusb3801_irq(int irq, void *data)
+{
+	struct wusb3801 *wusb3801 = data;
+	unsigned int dummy;
+
+	/*
+	 * The interrupt register must be read in order to clear the IRQ,
+	 * but all of the useful information is in the status register.
+	 */
+	regmap_read(wusb3801->regmap, WUSB3801_REG_INT, &dummy);
+
+	wusb3801_hw_update(wusb3801);
+
+	return IRQ_HANDLED;
+}
+
+static const struct regmap_config config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.max_register	= WUSB3801_REG_TEST12,
+};
+
+static int wusb3801_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct fwnode_handle *connector;
+	unsigned int device_id, test01;
+	struct wusb3801 *wusb3801;
+	const char *cap_str;
+	int ret;
+
+	wusb3801 = devm_kzalloc(dev, sizeof(*wusb3801), GFP_KERNEL);
+	if (!wusb3801)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, wusb3801);
+
+	wusb3801->dev = dev;
+
+	wusb3801->regmap = devm_regmap_init_i2c(client, &config);
+	if (IS_ERR(wusb3801->regmap))
+		return PTR_ERR(wusb3801->regmap);
+
+	regmap_read(wusb3801->regmap, WUSB3801_REG_DEVICE_ID, &device_id);
+	regmap_read(wusb3801->regmap, WUSB3801_REG_TEST01, &test01);
+	dev_info(dev, "Vendor ID: %ld, Version ID: %ld, Vendor SubID: 0x%02lx\n",
+		 device_id & WUSB3801_DEVICE_ID_VENDOR_ID,
+		 (device_id & WUSB3801_DEVICE_ID_VERSION_ID) >> 3,
+		 test01 & WUSB3801_TEST01_VENDOR_SUB_ID);
+
+	wusb3801->vbus_supply = devm_regulator_get(dev, "vbus");
+	if (IS_ERR(wusb3801->vbus_supply))
+		return PTR_ERR(wusb3801->vbus_supply);
+
+	connector = device_get_named_child_node(dev, "connector");
+	if (!connector)
+		return -ENODEV;
+
+	ret = typec_get_fw_cap(&wusb3801->cap, connector);
+	if (ret)
+		goto err_put_connector;
+	wusb3801->port_type = wusb3801->cap.type;
+
+	ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);
+	if (ret)
+		goto err_put_connector;
+
+	ret = typec_find_pwr_opmode(cap_str);
+	if (ret < 0 || ret == TYPEC_PWR_MODE_PD)
+		goto err_put_connector;
+	wusb3801->pwr_opmode = ret;
+
+	/* Initialize the hardware with the devicetree settings. */
+	ret = wusb3801_hw_init(wusb3801);
+	if (ret)
+		return ret;
+
+	wusb3801->cap.revision		= USB_TYPEC_REV_1_2;
+	wusb3801->cap.accessory[0]	= TYPEC_ACCESSORY_AUDIO;
+	wusb3801->cap.accessory[1]	= TYPEC_ACCESSORY_DEBUG;
+	wusb3801->cap.orientation_aware	= true;
+	wusb3801->cap.driver_data	= wusb3801;
+	wusb3801->cap.ops		= &wusb3801_typec_ops;
+
+	wusb3801->port = typec_register_port(dev, &wusb3801->cap);
+	if (IS_ERR(wusb3801->port)) {
+		ret = PTR_ERR(wusb3801->port);
+		goto err_put_connector;
+	}
+
+	/* Initialize the port attributes from the hardware state. */
+	wusb3801_hw_update(wusb3801);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL, wusb3801_irq,
+					IRQF_ONESHOT, dev_name(dev), wusb3801);
+	if (ret)
+		goto err_unregister_port;
+
+	fwnode_handle_put(connector);
+
+	return 0;
+
+err_unregister_port:
+	typec_unregister_port(wusb3801->port);
+err_put_connector:
+	fwnode_handle_put(connector);
+
+	return ret;
+}
+
+static int wusb3801_remove(struct i2c_client *client)
+{
+	struct wusb3801 *wusb3801 = i2c_get_clientdata(client);
+
+	if (wusb3801->partner)
+		typec_unregister_partner(wusb3801->partner);
+	typec_unregister_port(wusb3801->port);
+
+	if (wusb3801->vbus_on)
+		regulator_disable(wusb3801->vbus_supply);
+
+	return 0;
+}
+
+static const struct of_device_id wusb3801_of_match[] = {
+	{ .compatible = "willsemi,wusb3801" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, wusb3801_of_match);
+
+static struct i2c_driver wusb3801_driver = {
+	.probe_new	= wusb3801_probe,
+	.remove		= wusb3801_remove,
+	.driver		= {
+		.name		= "wusb3801",
+		.of_match_table	= wusb3801_of_match,
+	},
+};
+
+module_i2c_driver(wusb3801_driver);
+
+MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
+MODULE_DESCRIPTION("Willsemi WUSB3801 Type-C port controller driver");
+MODULE_LICENSE("GPL");
-- 
2.33.1


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

* Re: [PATCH 4/4] usb: typec: Support the WUSB3801 port controller
  2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
@ 2022-02-01 10:28   ` Greg Kroah-Hartman
  2022-02-01 10:29   ` Greg Kroah-Hartman
  2022-02-01 15:24   ` Guenter Roeck
  2 siblings, 0 replies; 9+ messages in thread
From: Greg Kroah-Hartman @ 2022-02-01 10:28 UTC (permalink / raw)
  To: Samuel Holland
  Cc: Heikki Krogerus, Guenter Roeck, Rob Herring, devicetree,
	linux-usb, linux-kernel

On Mon, Jan 31, 2022 at 09:24:40PM -0600, Samuel Holland wrote:
> +++ b/drivers/usb/typec/wusb3801.c
> @@ -0,0 +1,445 @@
> +// SPDX-License-Identifier: GPL-2.0+

I have to ask, are you sure you want this "GPLv2 and any future
version"?

thanks,

greg k-h

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

* Re: [PATCH 4/4] usb: typec: Support the WUSB3801 port controller
  2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
  2022-02-01 10:28   ` Greg Kroah-Hartman
@ 2022-02-01 10:29   ` Greg Kroah-Hartman
  2022-02-01 15:24   ` Guenter Roeck
  2 siblings, 0 replies; 9+ messages in thread
From: Greg Kroah-Hartman @ 2022-02-01 10:29 UTC (permalink / raw)
  To: Samuel Holland
  Cc: Heikki Krogerus, Guenter Roeck, Rob Herring, devicetree,
	linux-usb, linux-kernel

On Mon, Jan 31, 2022 at 09:24:40PM -0600, Samuel Holland wrote:
> +static enum typec_role wusb3801_get_default_role(struct wusb3801 *wusb3801)
> +{
> +	switch (wusb3801->port_type) {
> +	case TYPEC_PORT_SRC:
> +		return TYPEC_SOURCE;
> +	case TYPEC_PORT_SNK:
> +		return TYPEC_SINK;
> +	case TYPEC_PORT_DRP:
> +	default:
> +		if (wusb3801->cap.prefer_role == TYPEC_SOURCE)
> +			return TYPEC_SOURCE;
> +		return TYPEC_SINK;
> +	}
> +}
> +
> +static int wusb3801_map_port_type(enum typec_port_type type)
> +{
> +	switch (type) {
> +	case TYPEC_PORT_SRC:
> +		return WUSB3801_CTRL0_ROLE_SRC;
> +	case TYPEC_PORT_SNK:
> +		return WUSB3801_CTRL0_ROLE_SNK;
> +	case TYPEC_PORT_DRP:
> +	default:
> +		return WUSB3801_CTRL0_ROLE_DRP;
> +	}
> +}
> +
> +static int wusb3801_map_pwr_opmode(enum typec_pwr_opmode mode)
> +{
> +	switch (mode) {
> +	case TYPEC_PWR_MODE_USB:
> +	default:
> +		return WUSB3801_CTRL0_CURRENT_DEFAULT;
> +	case TYPEC_PWR_MODE_1_5A:
> +		return WUSB3801_CTRL0_CURRENT_1_5A;
> +	case TYPEC_PWR_MODE_3_0A:
> +		return WUSB3801_CTRL0_CURRENT_3_0A;
> +	}
> +}
> +
> +static unsigned int wusb3801_map_try_role(int role)
> +{
> +	switch (role) {
> +	case TYPEC_NO_PREFERRED_ROLE:
> +	default:
> +		return WUSB3801_CTRL0_TRY_NONE;
> +	case TYPEC_SINK:
> +		return WUSB3801_CTRL0_TRY_SNK;
> +	case TYPEC_SOURCE:
> +		return WUSB3801_CTRL0_TRY_SRC;
> +	}
> +}
> +
> +static enum typec_orientation wusb3801_unmap_orientation(unsigned int status)
> +{
> +	switch (status & WUSB3801_STAT_ORIENTATION) {
> +	case WUSB3801_STAT_ORIENTATION_NONE:
> +	case WUSB3801_STAT_ORIENTATION_BOTH:
> +		return TYPEC_ORIENTATION_NONE;
> +	case WUSB3801_STAT_ORIENTATION_CC1:
> +		return TYPEC_ORIENTATION_NORMAL;
> +	case WUSB3801_STAT_ORIENTATION_CC2:
> +		return TYPEC_ORIENTATION_REVERSE;
> +	}
> +
> +	unreachable();

Some of these functions use use unreachable() and others you do not.
Pick one style and stick with it please.

thanks,

greg k-h

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

* Re: [PATCH 4/4] usb: typec: Support the WUSB3801 port controller
  2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
  2022-02-01 10:28   ` Greg Kroah-Hartman
  2022-02-01 10:29   ` Greg Kroah-Hartman
@ 2022-02-01 15:24   ` Guenter Roeck
  2 siblings, 0 replies; 9+ messages in thread
From: Guenter Roeck @ 2022-02-01 15:24 UTC (permalink / raw)
  To: Samuel Holland, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	devicetree
  Cc: linux-usb, linux-kernel

On 1/31/22 19:24, Samuel Holland wrote:
> WUSB3801 features a configurable port type, accessory detection, and
> plug orientation detection. It provides a hardware "ID" pin output for
> compatibility with USB 2.0 OTG PHYs. Add a typec class driver for it.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> 
>   drivers/usb/typec/Kconfig    |  10 +
>   drivers/usb/typec/Makefile   |   1 +
>   drivers/usb/typec/wusb3801.c | 445 +++++++++++++++++++++++++++++++++++
>   3 files changed, 456 insertions(+)
>   create mode 100644 drivers/usb/typec/wusb3801.c
> 
> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> index ab480f38523a..74acde9fc09f 100644
> --- a/drivers/usb/typec/Kconfig
> +++ b/drivers/usb/typec/Kconfig
> @@ -88,6 +88,16 @@ config TYPEC_QCOM_PMIC
>   	  It will also enable the VBUS output to connected devices when a
>   	  DFP connection is made.
>   
> +config TYPEC_WUSB3801
> +	tristate "Willsemi WUSB3801 Type-C port controller driver"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  Say Y or M here if your system has a WUSB3801 Type-C port controller.
> +
> +	  If you choose to build this driver as a dynamically linked module, the
> +	  module will be called wusb3801.ko.
> +
>   source "drivers/usb/typec/mux/Kconfig"
>   
>   source "drivers/usb/typec/altmodes/Kconfig"
> diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
> index 57870a2bd787..3b722ec28d99 100644
> --- a/drivers/usb/typec/Makefile
> +++ b/drivers/usb/typec/Makefile
> @@ -9,4 +9,5 @@ obj-$(CONFIG_TYPEC_TPS6598X)	+= tipd/
>   obj-$(CONFIG_TYPEC_HD3SS3220)	+= hd3ss3220.o
>   obj-$(CONFIG_TYPEC_QCOM_PMIC)	+= qcom-pmic-typec.o
>   obj-$(CONFIG_TYPEC_STUSB160X) 	+= stusb160x.o
> +obj-$(CONFIG_TYPEC_WUSB3801)	+= wusb3801.o
>   obj-$(CONFIG_TYPEC)		+= mux/
> diff --git a/drivers/usb/typec/wusb3801.c b/drivers/usb/typec/wusb3801.c
> new file mode 100644
> index 000000000000..e4ff37bcce9c
> --- /dev/null
> +++ b/drivers/usb/typec/wusb3801.c
> @@ -0,0 +1,445 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Willsemi WUSB3801 Type-C port controller driver
> + *
> + * Copyright (C) 2022 Samuel Holland <samuel@sholland.org>
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/usb/typec.h>
> +
> +#define WUSB3801_REG_DEVICE_ID		0x01
> +#define WUSB3801_REG_CTRL0		0x02
> +#define WUSB3801_REG_INT		0x03
> +#define WUSB3801_REG_STAT		0x04
> +#define WUSB3801_REG_CTRL1		0x05
> +#define WUSB3801_REG_TEST00		0x06
> +#define WUSB3801_REG_TEST01		0x07
> +#define WUSB3801_REG_TEST02		0x08
> +#define WUSB3801_REG_TEST03		0x09
> +#define WUSB3801_REG_TEST04		0x0a
> +#define WUSB3801_REG_TEST05		0x0b
> +#define WUSB3801_REG_TEST06		0x0c
> +#define WUSB3801_REG_TEST07		0x0d
> +#define WUSB3801_REG_TEST08		0x0e
> +#define WUSB3801_REG_TEST09		0x0f
> +#define WUSB3801_REG_TEST0A		0x10
> +#define WUSB3801_REG_TEST0B		0x11
> +#define WUSB3801_REG_TEST0C		0x12
> +#define WUSB3801_REG_TEST0D		0x13
> +#define WUSB3801_REG_TEST0E		0x14
> +#define WUSB3801_REG_TEST0F		0x15
> +#define WUSB3801_REG_TEST10		0x16
> +#define WUSB3801_REG_TEST11		0x17
> +#define WUSB3801_REG_TEST12		0x18
> +
> +#define WUSB3801_DEVICE_ID_VERSION_ID	GENMASK(7, 3)
> +#define WUSB3801_DEVICE_ID_VENDOR_ID	GENMASK(2, 0)
> +
> +#define WUSB3801_CTRL0_DIS_ACC_SUPPORT	BIT(7)
> +#define WUSB3801_CTRL0_TRY		GENMASK(6, 5)
> +#define WUSB3801_CTRL0_TRY_NONE		(0x0 << 5)
> +#define WUSB3801_CTRL0_TRY_SNK		(0x1 << 5)
> +#define WUSB3801_CTRL0_TRY_SRC		(0x2 << 5)
> +#define WUSB3801_CTRL0_CURRENT		GENMASK(4, 3) /* SRC */
> +#define WUSB3801_CTRL0_CURRENT_DEFAULT	(0x0 << 3)
> +#define WUSB3801_CTRL0_CURRENT_1_5A	(0x1 << 3)
> +#define WUSB3801_CTRL0_CURRENT_3_0A	(0x2 << 3)
> +#define WUSB3801_CTRL0_ROLE		GENMASK(2, 1)
> +#define WUSB3801_CTRL0_ROLE_SNK		(0x0 << 1)
> +#define WUSB3801_CTRL0_ROLE_SRC		(0x1 << 1)
> +#define WUSB3801_CTRL0_ROLE_DRP		(0x2 << 1)
> +#define WUSB3801_CTRL0_INT_MASK		BIT(0)
> +
> +#define WUSB3801_INT_ATTACHED		BIT(0)
> +#define WUSB3801_INT_DETACHED		BIT(1)
> +
> +#define WUSB3801_STAT_VBUS_DETECTED	BIT(7)
> +#define WUSB3801_STAT_CURRENT		GENMASK(6, 5) /* SNK */
> +#define WUSB3801_STAT_CURRENT_STANDBY	(0x0 << 5)
> +#define WUSB3801_STAT_CURRENT_DEFAULT	(0x1 << 5)
> +#define WUSB3801_STAT_CURRENT_1_5A	(0x2 << 5)
> +#define WUSB3801_STAT_CURRENT_3_0A	(0x3 << 5)
> +#define WUSB3801_STAT_PARTNER		GENMASK(4, 2)
> +#define WUSB3801_STAT_PARTNER_STANDBY	(0x0 << 2)
> +#define WUSB3801_STAT_PARTNER_SNK	(0x1 << 2)
> +#define WUSB3801_STAT_PARTNER_SRC	(0x2 << 2)
> +#define WUSB3801_STAT_PARTNER_AUDIO	(0x3 << 2)
> +#define WUSB3801_STAT_PARTNER_DEBUG	(0x4 << 2)
> +#define WUSB3801_STAT_ORIENTATION	GENMASK(1, 0)
> +#define WUSB3801_STAT_ORIENTATION_NONE	(0x0 << 0)
> +#define WUSB3801_STAT_ORIENTATION_CC1	(0x1 << 0)
> +#define WUSB3801_STAT_ORIENTATION_CC2	(0x2 << 0)
> +#define WUSB3801_STAT_ORIENTATION_BOTH	(0x3 << 0)
> +
> +#define WUSB3801_CTRL1_SM_RESET		BIT(0)
> +
> +#define WUSB3801_TEST01_VENDOR_SUB_ID	(BIT(8) | BIT(6))
> +
> +#define WUSB3801_TEST02_FORCE_ERR_RCY	BIT(8)
> +
> +#define WUSB3801_TEST0A_WAIT_VBUS	BIT(5)
> +
> +struct wusb3801 {
> +	struct typec_capability	cap;
> +	struct device		*dev;
> +	struct typec_partner	*partner;
> +	struct typec_port	*port;
> +	struct regmap		*regmap;
> +	struct regulator	*vbus_supply;
> +	unsigned int		partner_type;
> +	enum typec_port_type	port_type;
> +	enum typec_pwr_opmode	pwr_opmode;
> +	bool			vbus_on;
> +};
> +
> +static enum typec_role wusb3801_get_default_role(struct wusb3801 *wusb3801)
> +{
> +	switch (wusb3801->port_type) {
> +	case TYPEC_PORT_SRC:
> +		return TYPEC_SOURCE;
> +	case TYPEC_PORT_SNK:
> +		return TYPEC_SINK;
> +	case TYPEC_PORT_DRP:
> +	default:
> +		if (wusb3801->cap.prefer_role == TYPEC_SOURCE)
> +			return TYPEC_SOURCE;
> +		return TYPEC_SINK;
> +	}
> +}
> +
> +static int wusb3801_map_port_type(enum typec_port_type type)
> +{
> +	switch (type) {
> +	case TYPEC_PORT_SRC:
> +		return WUSB3801_CTRL0_ROLE_SRC;
> +	case TYPEC_PORT_SNK:
> +		return WUSB3801_CTRL0_ROLE_SNK;
> +	case TYPEC_PORT_DRP:
> +	default:
> +		return WUSB3801_CTRL0_ROLE_DRP;
> +	}
> +}
> +
> +static int wusb3801_map_pwr_opmode(enum typec_pwr_opmode mode)
> +{
> +	switch (mode) {
> +	case TYPEC_PWR_MODE_USB:
> +	default:
> +		return WUSB3801_CTRL0_CURRENT_DEFAULT;
> +	case TYPEC_PWR_MODE_1_5A:
> +		return WUSB3801_CTRL0_CURRENT_1_5A;
> +	case TYPEC_PWR_MODE_3_0A:
> +		return WUSB3801_CTRL0_CURRENT_3_0A;
> +	}
> +}
> +
> +static unsigned int wusb3801_map_try_role(int role)
> +{
> +	switch (role) {
> +	case TYPEC_NO_PREFERRED_ROLE:
> +	default:
> +		return WUSB3801_CTRL0_TRY_NONE;
> +	case TYPEC_SINK:
> +		return WUSB3801_CTRL0_TRY_SNK;
> +	case TYPEC_SOURCE:
> +		return WUSB3801_CTRL0_TRY_SRC;
> +	}
> +}
> +
> +static enum typec_orientation wusb3801_unmap_orientation(unsigned int status)
> +{
> +	switch (status & WUSB3801_STAT_ORIENTATION) {
> +	case WUSB3801_STAT_ORIENTATION_NONE:
> +	case WUSB3801_STAT_ORIENTATION_BOTH:
> +		return TYPEC_ORIENTATION_NONE;
> +	case WUSB3801_STAT_ORIENTATION_CC1:
> +		return TYPEC_ORIENTATION_NORMAL;
> +	case WUSB3801_STAT_ORIENTATION_CC2:
> +		return TYPEC_ORIENTATION_REVERSE;
> +	}
> +
> +	unreachable();
> +}
> +
> +static enum typec_pwr_opmode wusb3801_unmap_pwr_opmode(unsigned int status)
> +{
> +	switch (status & WUSB3801_STAT_CURRENT) {
> +	case WUSB3801_STAT_CURRENT_STANDBY:
> +	case WUSB3801_STAT_CURRENT_DEFAULT:
> +		return TYPEC_PWR_MODE_USB;
> +	case WUSB3801_STAT_CURRENT_1_5A:
> +		return TYPEC_PWR_MODE_1_5A;
> +	case WUSB3801_STAT_CURRENT_3_0A:
> +		return TYPEC_PWR_MODE_3_0A;
> +	}
> +
> +	unreachable();
> +}
> +
> +static int wusb3801_try_role(struct typec_port *port, int role)
> +{
> +	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
> +
> +	return regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
> +				  WUSB3801_CTRL0_TRY,
> +				  wusb3801_map_try_role(role));
> +}
> +
> +static int wusb3801_port_type_set(struct typec_port *port,
> +				  enum typec_port_type type)
> +{
> +	struct wusb3801 *wusb3801 = typec_get_drvdata(port);
> +	int ret;
> +
> +	ret = regmap_update_bits(wusb3801->regmap, WUSB3801_REG_CTRL0,
> +				 WUSB3801_CTRL0_ROLE,
> +				 wusb3801_map_port_type(type));
> +	if (ret)
> +		return ret;
> +
> +	wusb3801->port_type = type;
> +
> +	return 0;
> +}
> +
> +static const struct typec_operations wusb3801_typec_ops = {
> +	.try_role	= wusb3801_try_role,
> +	.port_type_set	= wusb3801_port_type_set,
> +};
> +
> +static int wusb3801_hw_init(struct wusb3801 *wusb3801)
> +{
> +	return regmap_write(wusb3801->regmap, WUSB3801_REG_CTRL0,
> +			    wusb3801_map_try_role(wusb3801->cap.prefer_role) |
> +			    wusb3801_map_pwr_opmode(wusb3801->pwr_opmode) |
> +			    wusb3801_map_port_type(wusb3801->port_type));
> +}
> +
> +static void wusb3801_hw_update(struct wusb3801 *wusb3801)
> +{
> +	struct typec_port *port = wusb3801->port;
> +	struct device *dev = wusb3801->dev;
> +	unsigned int partner_type, status;
> +	int ret;
> +
> +	ret = regmap_read(wusb3801->regmap, WUSB3801_REG_STAT, &status);
> +	if (ret) {
> +		dev_warn(dev, "Failed to read port status: %d\n", ret);
> +		status = 0;
> +	}
> +	dev_dbg(dev, "status = 0x%02x\n", status);
> +
> +	partner_type = status & WUSB3801_STAT_PARTNER;
> +
> +	if (partner_type == WUSB3801_STAT_PARTNER_SNK) {
> +		if (!wusb3801->vbus_on) {
> +			ret = regulator_enable(wusb3801->vbus_supply);
> +			if (ret)
> +				dev_warn(dev, "Failed to enable VBUS: %d\n", ret);
> +			wusb3801->vbus_on = true;
> +		}
> +	} else {
> +		if (wusb3801->vbus_on) {
> +			regulator_disable(wusb3801->vbus_supply);
> +			wusb3801->vbus_on = false;
> +		}
> +	}
> +
> +	if (partner_type != wusb3801->partner_type) {
> +		struct typec_partner_desc desc = {};
> +		enum typec_data_role data_role;
> +		enum typec_role pwr_role = wusb3801_get_default_role(wusb3801);
> +
> +		switch (partner_type) {
> +		case WUSB3801_STAT_PARTNER_STANDBY:
> +			break;
> +		case WUSB3801_STAT_PARTNER_SNK:
> +			pwr_role = TYPEC_SOURCE;
> +			break;
> +		case WUSB3801_STAT_PARTNER_SRC:
> +			pwr_role = TYPEC_SINK;
> +			break;
> +		case WUSB3801_STAT_PARTNER_AUDIO:
> +			desc.accessory = TYPEC_ACCESSORY_AUDIO;
> +			break;
> +		case WUSB3801_STAT_PARTNER_DEBUG:
> +			desc.accessory = TYPEC_ACCESSORY_DEBUG;
> +			break;
> +		}
> +
> +		if (wusb3801->partner) {
> +			typec_unregister_partner(wusb3801->partner);
> +			wusb3801->partner = NULL;
> +		}
> +
> +		if (partner_type != WUSB3801_STAT_PARTNER_STANDBY) {
> +			wusb3801->partner = typec_register_partner(port, &desc);
> +			if (IS_ERR(wusb3801->partner))
> +				dev_err(dev, "Failed to register partner: %ld\n",
> +					PTR_ERR(wusb3801->partner));
> +		}
> +
> +		data_role = pwr_role == TYPEC_SOURCE ? TYPEC_HOST : TYPEC_DEVICE;
> +		typec_set_data_role(port, data_role);
> +		typec_set_pwr_role(port, pwr_role);
> +		typec_set_vconn_role(port, pwr_role);
> +	}
> +
> +	typec_set_pwr_opmode(wusb3801->port,
> +			     partner_type == WUSB3801_STAT_PARTNER_SRC
> +				? wusb3801_unmap_pwr_opmode(status)
> +				: wusb3801->pwr_opmode);
> +	typec_set_orientation(wusb3801->port,
> +			      wusb3801_unmap_orientation(status));
> +
> +	wusb3801->partner_type = partner_type;
> +}
> +
> +static irqreturn_t wusb3801_irq(int irq, void *data)
> +{
> +	struct wusb3801 *wusb3801 = data;
> +	unsigned int dummy;
> +
> +	/*
> +	 * The interrupt register must be read in order to clear the IRQ,
> +	 * but all of the useful information is in the status register.
> +	 */
> +	regmap_read(wusb3801->regmap, WUSB3801_REG_INT, &dummy);
> +
> +	wusb3801_hw_update(wusb3801);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct regmap_config config = {
> +	.reg_bits	= 8,
> +	.val_bits	= 8,
> +	.max_register	= WUSB3801_REG_TEST12,
> +};
> +
> +static int wusb3801_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct fwnode_handle *connector;
> +	unsigned int device_id, test01;
> +	struct wusb3801 *wusb3801;
> +	const char *cap_str;
> +	int ret;
> +
> +	wusb3801 = devm_kzalloc(dev, sizeof(*wusb3801), GFP_KERNEL);
> +	if (!wusb3801)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, wusb3801);
> +
> +	wusb3801->dev = dev;
> +
> +	wusb3801->regmap = devm_regmap_init_i2c(client, &config);
> +	if (IS_ERR(wusb3801->regmap))
> +		return PTR_ERR(wusb3801->regmap);
> +
> +	regmap_read(wusb3801->regmap, WUSB3801_REG_DEVICE_ID, &device_id);
> +	regmap_read(wusb3801->regmap, WUSB3801_REG_TEST01, &test01);
> +	dev_info(dev, "Vendor ID: %ld, Version ID: %ld, Vendor SubID: 0x%02lx\n",
> +		 device_id & WUSB3801_DEVICE_ID_VENDOR_ID,
> +		 (device_id & WUSB3801_DEVICE_ID_VERSION_ID) >> 3,
> +		 test01 & WUSB3801_TEST01_VENDOR_SUB_ID);
> +
> +	wusb3801->vbus_supply = devm_regulator_get(dev, "vbus");
> +	if (IS_ERR(wusb3801->vbus_supply))
> +		return PTR_ERR(wusb3801->vbus_supply);
> +
> +	connector = device_get_named_child_node(dev, "connector");
> +	if (!connector)
> +		return -ENODEV;
> +
> +	ret = typec_get_fw_cap(&wusb3801->cap, connector);
> +	if (ret)
> +		goto err_put_connector;
> +	wusb3801->port_type = wusb3801->cap.type;
> +
> +	ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);
> +	if (ret)
> +		goto err_put_connector;
> +
> +	ret = typec_find_pwr_opmode(cap_str);
> +	if (ret < 0 || ret == TYPEC_PWR_MODE_PD)
> +		goto err_put_connector;
> +	wusb3801->pwr_opmode = ret;
> +
> +	/* Initialize the hardware with the devicetree settings. */
> +	ret = wusb3801_hw_init(wusb3801);
> +	if (ret)
> +		return ret;
> +
> +	wusb3801->cap.revision		= USB_TYPEC_REV_1_2;
> +	wusb3801->cap.accessory[0]	= TYPEC_ACCESSORY_AUDIO;
> +	wusb3801->cap.accessory[1]	= TYPEC_ACCESSORY_DEBUG;
> +	wusb3801->cap.orientation_aware	= true;
> +	wusb3801->cap.driver_data	= wusb3801;
> +	wusb3801->cap.ops		= &wusb3801_typec_ops;
> +
> +	wusb3801->port = typec_register_port(dev, &wusb3801->cap);
> +	if (IS_ERR(wusb3801->port)) {
> +		ret = PTR_ERR(wusb3801->port);
> +		goto err_put_connector;
> +	}
> +
> +	/* Initialize the port attributes from the hardware state. */
> +	wusb3801_hw_update(wusb3801);
> +
> +	ret = devm_request_threaded_irq(dev, client->irq, NULL, wusb3801_irq,
> +					IRQF_ONESHOT, dev_name(dev), wusb3801);

Are you sure you can use a devm function here ? That means the typec port
will be unregistered in the remove function before the interrupt is released.

Guenter

> +	if (ret)
> +		goto err_unregister_port;
> +
> +	fwnode_handle_put(connector);
> +
> +	return 0;
> +
> +err_unregister_port:
> +	typec_unregister_port(wusb3801->port);
> +err_put_connector:
> +	fwnode_handle_put(connector);
> +
> +	return ret;
> +}
> +
> +static int wusb3801_remove(struct i2c_client *client)
> +{
> +	struct wusb3801 *wusb3801 = i2c_get_clientdata(client);
> +
> +	if (wusb3801->partner)
> +		typec_unregister_partner(wusb3801->partner);
> +	typec_unregister_port(wusb3801->port);
> +
> +	if (wusb3801->vbus_on)
> +		regulator_disable(wusb3801->vbus_supply);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id wusb3801_of_match[] = {
> +	{ .compatible = "willsemi,wusb3801" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, wusb3801_of_match);
> +
> +static struct i2c_driver wusb3801_driver = {
> +	.probe_new	= wusb3801_probe,
> +	.remove		= wusb3801_remove,
> +	.driver		= {
> +		.name		= "wusb3801",
> +		.of_match_table	= wusb3801_of_match,
> +	},
> +};
> +
> +module_i2c_driver(wusb3801_driver);
> +
> +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
> +MODULE_DESCRIPTION("Willsemi WUSB3801 Type-C port controller driver");
> +MODULE_LICENSE("GPL");


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

* Re: [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties
  2022-02-01  3:24 ` [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties Samuel Holland
@ 2022-02-02  6:22   ` kernel test robot
  2022-02-02 19:53     ` Guenter Roeck
  0 siblings, 1 reply; 9+ messages in thread
From: kernel test robot @ 2022-02-02  6:22 UTC (permalink / raw)
  To: Samuel Holland, Heikki Krogerus, Guenter Roeck,
	Greg Kroah-Hartman, Rob Herring, devicetree
  Cc: kbuild-all, linux-usb, linux-kernel, Samuel Holland

Hi Samuel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on robh/for-next v5.17-rc2 next-20220202]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Samuel-Holland/dt-bindings-vendor-prefixes-Add-willsemi/20220201-112541
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: openrisc-randconfig-m031-20220201 (https://download.01.org/0day-ci/archive/20220202/202202021458.xcH4F4SQ-lkp@intel.com/config)
compiler: or1k-linux-gcc (GCC) 11.2.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

smatch warnings:
drivers/usb/typec/class.c:1919 typec_get_fw_cap() warn: unsigned 'cap->type' is never less than zero.
drivers/usb/typec/class.c:1926 typec_get_fw_cap() warn: unsigned 'cap->data' is never less than zero.

vim +1919 drivers/usb/typec/class.c

  1896	
  1897	int typec_get_fw_cap(struct typec_capability *cap,
  1898			     struct fwnode_handle *fwnode)
  1899	{
  1900		const char *cap_str;
  1901		int ret;
  1902	
  1903		/*
  1904		 * This fwnode has a "compatible" property, but is never populated as a
  1905		 * struct device. Instead we simply parse it to read the properties.
  1906		 * This it breaks fw_devlink=on. To maintain backward compatibility
  1907		 * with existing DT files, we work around this by deleting any
  1908		 * fwnode_links to/from this fwnode.
  1909		 */
  1910		fw_devlink_purge_absent_suppliers(fwnode);
  1911	
  1912		cap->fwnode = fwnode;
  1913	
  1914		ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
  1915		if (ret < 0)
  1916			return ret;
  1917	
  1918		cap->type = typec_find_port_power_role(cap_str);
> 1919		if (cap->type < 0)
  1920			return cap->type;
  1921	
  1922		/* USB data support is optional */
  1923		ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
  1924		if (ret == 0) {
  1925			cap->data = typec_find_port_data_role(cap_str);
> 1926			if (cap->data < 0)
  1927				return cap->data;
  1928		}
  1929	
  1930		/* Get the preferred power role for a DRP */
  1931		if (cap->type == TYPEC_PORT_DRP) {
  1932			cap->prefer_role = TYPEC_NO_PREFERRED_ROLE;
  1933	
  1934			ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str);
  1935			if (ret == 0) {
  1936				cap->prefer_role = typec_find_power_role(cap_str);
  1937				if (cap->prefer_role < 0)
  1938					return cap->prefer_role;
  1939			}
  1940		}
  1941	
  1942		return 0;
  1943	}
  1944	EXPORT_SYMBOL_GPL(typec_get_fw_cap);
  1945	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

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

* Re: [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties
  2022-02-02  6:22   ` kernel test robot
@ 2022-02-02 19:53     ` Guenter Roeck
  0 siblings, 0 replies; 9+ messages in thread
From: Guenter Roeck @ 2022-02-02 19:53 UTC (permalink / raw)
  To: kernel test robot
  Cc: Samuel Holland, Heikki Krogerus, Greg Kroah-Hartman, Rob Herring,
	devicetree, kbuild-all, linux-usb, linux-kernel

On Wed, Feb 02, 2022 at 02:22:27PM +0800, kernel test robot wrote:
> Hi Samuel,
> 
> Thank you for the patch! Perhaps something to improve:
> 
> [auto build test WARNING on usb/usb-testing]
> [also build test WARNING on robh/for-next v5.17-rc2 next-20220202]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch]
> 
> url:    https://github.com/0day-ci/linux/commits/Samuel-Holland/dt-bindings-vendor-prefixes-Add-willsemi/20220201-112541
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
> config: openrisc-randconfig-m031-20220201 (https://download.01.org/0day-ci/archive/20220202/202202021458.xcH4F4SQ-lkp@intel.com/config)
> compiler: or1k-linux-gcc (GCC) 11.2.0
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> smatch warnings:
> drivers/usb/typec/class.c:1919 typec_get_fw_cap() warn: unsigned 'cap->type' is never less than zero.
> drivers/usb/typec/class.c:1926 typec_get_fw_cap() warn: unsigned 'cap->data' is never less than zero.
> 

Ah yes, there was a reason to assign the return values to 'ret' first
and only afterwards to the actual capabilities. Please fix and resubmit.

Guenter

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

end of thread, other threads:[~2022-02-02 19:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-01  3:24 [PATCH 1/4] dt-bindings: vendor-prefixes: Add willsemi Samuel Holland
2022-02-01  3:24 ` [PATCH 2/4] dt-bindings: usb: Add WUSB3801 Type-C Port Controller Samuel Holland
2022-02-01  3:24 ` [PATCH 3/4] usb: typec: Factor out non-PD fwnode properties Samuel Holland
2022-02-02  6:22   ` kernel test robot
2022-02-02 19:53     ` Guenter Roeck
2022-02-01  3:24 ` [PATCH 4/4] usb: typec: Support the WUSB3801 port controller Samuel Holland
2022-02-01 10:28   ` Greg Kroah-Hartman
2022-02-01 10:29   ` Greg Kroah-Hartman
2022-02-01 15:24   ` Guenter Roeck

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).