[PATCH v2 2/2] usb: add basic TUSB320 Type-C controller support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We import here the Linux TUSB320 driver, so board code and user scripts
have access to following variables:

  - $dev.usb_role = { device, host }
  - $dev.pwr_role = { sink, source }
  - $dev.accessory = { none, audio, debug }

These can be used to decide whether to switch a USB-C debug mux for
example.

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
v1 -> v2:
  - split off from first patch
  - include <linux/bitops.h>
---
 Documentation/user/usb.rst  |   3 +
 drivers/usb/typec/Kconfig   |   8 ++
 drivers/usb/typec/Makefile  |   1 +
 drivers/usb/typec/tusb320.c | 221 ++++++++++++++++++++++++++++++++++++
 4 files changed, 233 insertions(+)
 create mode 100644 drivers/usb/typec/tusb320.c

diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
index 20e223da93b5..2474ccb54b67 100644
--- a/Documentation/user/usb.rst
+++ b/Documentation/user/usb.rst
@@ -259,6 +259,9 @@ export a number of device parameters:
 - ``$typec0.pwr_role`` = { ``sink``, ``source`` }
 - ``$typec0.accessory`` = { ``none``, ``audio``, ``debug`` }
 
+Currently, only the TUSB320 is supported, but it's straight-forward to port more
+drivers from Linux.
+
 USB Gadget autostart Support
 ----------------------------
 
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 56f10e6ca549..3b32a4e05a91 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -4,3 +4,11 @@
 config TYPEC
 	prompt "Compile USB Type-C framework support" if COMPILE_TEST
 	bool
+
+config TYPEC_TUSB320
+	tristate "TI TUSB320 Type-C port controller"
+	depends on I2C
+	select REGMAP_I2C
+	select TYPEC
+	help
+	  Say Y or here if your system has a TI TUSB320 Type-C port controller.
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 6b8347f10c06..456b94afbf6f 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_TYPEC)		+= class.o
+obj-$(CONFIG_TYPEC_TUSB320)	+= tusb320.o
diff --git a/drivers/usb/typec/tusb320.c b/drivers/usb/typec/tusb320.c
new file mode 100644
index 000000000000..eb6b492481a3
--- /dev/null
+++ b/drivers/usb/typec/tusb320.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on the Linux driver:
+ *   drivers/typec/typec-tusb320.c - TUSB320 typec driver
+ *
+ * Copyright (C) 2020 National Instruments Corporation
+ * Author: Michael Auchter <michael.auchter@xxxxxx>
+ */
+
+#include <linux/bitfield.h>
+#include <i2c/i2c.h>
+#include <init.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <module.h>
+#include <regmap.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+
+#define TUSB320_REG8				0x8
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE	GENMASK(7, 6)
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_USB	0x0
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_15A	0x1
+#define TUSB320_REG8_CURRENT_MODE_ADVERTISE_30A	0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT	GENMASK(5, 4)
+#define TUSB320_REG8_CURRENT_MODE_DETECT_DEF	0x0
+#define TUSB320_REG8_CURRENT_MODE_DETECT_MED	0x1
+#define TUSB320_REG8_CURRENT_MODE_DETECT_ACC	0x2
+#define TUSB320_REG8_CURRENT_MODE_DETECT_HI	0x3
+#define TUSB320_REG8_ACCESSORY_CONNECTED	GENMASK(3, 1)
+#define TUSB320_REG8_ACCESSORY_CONNECTED_NONE	0x0
+#define TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO	0x4
+#define TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG	0x5
+#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP	0x6
+#define TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP	0x7
+#define TUSB320_REG8_ACTIVE_CABLE_DETECTION	BIT(0)
+
+#define TUSB320_REG9				0x9
+#define TUSB320_REG9_ATTACHED_STATE		GENMASK(7, 6)
+#define TUSB320_REG9_CABLE_DIRECTION		BIT(5)
+#define TUSB320_REG9_INTERRUPT_STATUS		BIT(4)
+
+enum tusb320_attached_state {
+	TUSB320_ATTACHED_STATE_NONE,
+	TUSB320_ATTACHED_STATE_DFP,
+	TUSB320_ATTACHED_STATE_UFP,
+	TUSB320_ATTACHED_STATE_ACC,
+};
+
+struct tusb320_priv {
+	struct device *dev;
+	struct regmap *regmap;
+	struct typec_port *port;
+	struct typec_capability	cap;
+};
+
+static int tusb320_typec_irq_handler(struct tusb320_priv *priv, u8 reg9)
+{
+	struct typec_port *port = priv->port;
+	int typec_mode;
+	enum typec_role pwr_role;
+	enum usb_role usb_role;
+	u8 state, accessory;
+	int ret, reg8;
+
+	ret = regmap_read(priv->regmap, TUSB320_REG8, &reg8);
+	if (ret)
+		return ret;
+
+	state = FIELD_GET(TUSB320_REG9_ATTACHED_STATE, reg9);
+	accessory = FIELD_GET(TUSB320_REG8_ACCESSORY_CONNECTED, reg8);
+
+	switch (state) {
+	case TUSB320_ATTACHED_STATE_DFP:
+		typec_mode = TYPEC_MODE_USB2;
+		usb_role = USB_ROLE_HOST;
+		pwr_role = TYPEC_SOURCE;
+		break;
+	case TUSB320_ATTACHED_STATE_UFP:
+		typec_mode = TYPEC_MODE_USB2;
+		usb_role = USB_ROLE_DEVICE;
+		pwr_role = TYPEC_SINK;
+		break;
+	case TUSB320_ATTACHED_STATE_ACC:
+		/*
+		 * Accessory detected. For debug accessories, just make some
+		 * qualified guesses as to the role for lack of a better option.
+		 */
+		if (accessory == TUSB320_REG8_ACCESSORY_CONNECTED_AUDIO ||
+		    accessory == TUSB320_REG8_ACCESSORY_CONNECTED_ACHRG) {
+			typec_mode = TYPEC_MODE_AUDIO;
+			usb_role = USB_ROLE_NONE;
+			pwr_role = TYPEC_SINK;
+			break;
+		} else if (accessory ==
+			   TUSB320_REG8_ACCESSORY_CONNECTED_DBGDFP) {
+			typec_mode = TYPEC_MODE_DEBUG;
+			pwr_role = TYPEC_SOURCE;
+			usb_role = USB_ROLE_HOST;
+			break;
+		} else if (accessory ==
+			   TUSB320_REG8_ACCESSORY_CONNECTED_DBGUFP) {
+			typec_mode = TYPEC_MODE_DEBUG;
+			pwr_role = TYPEC_SINK;
+			usb_role = USB_ROLE_DEVICE;
+			break;
+		}
+
+		dev_warn(priv->dev, "unexpected ACCESSORY_CONNECTED state %d\n",
+			 accessory);
+
+		fallthrough;
+	default:
+		typec_mode = TYPEC_MODE_USB2;
+		usb_role = USB_ROLE_NONE;
+		pwr_role = TYPEC_SINK;
+		break;
+	}
+
+	typec_set_pwr_role(port, pwr_role);
+	typec_set_mode(port, typec_mode);
+	typec_set_role(port, usb_role);
+
+	return 0;
+}
+
+static int tusb320_state_update_handler(struct tusb320_priv *priv,
+					bool force_update)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(priv->regmap, TUSB320_REG9, &reg);
+	if (ret)
+		return ret;
+
+	if (!force_update && !(reg & TUSB320_REG9_INTERRUPT_STATUS))
+		return 0;
+
+	ret = tusb320_typec_irq_handler(priv, reg);
+
+	regmap_write(priv->regmap, TUSB320_REG9, reg);
+
+	return ret;
+}
+
+static irqreturn_t tusb320_irq_handler(struct typec_port *port)
+{
+	struct tusb320_priv *priv = typec_get_drvdata(port);
+
+	return tusb320_state_update_handler(priv, false);
+}
+
+static const struct typec_operations tusb320_typec_ops = {
+	.poll	= tusb320_irq_handler,
+};
+
+static const struct regmap_config tusb320_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int tusb320_typec_probe(struct i2c_client *client,
+			       struct tusb320_priv *priv)
+{
+	struct device_node *connector;
+
+	connector = of_get_child_by_name(client->dev.of_node, "connector");
+
+	priv->cap.driver_data		= priv;
+	priv->cap.ops			= &tusb320_typec_ops;
+	priv->cap.of_node		= connector;
+
+	priv->port = typec_register_port(&client->dev, &priv->cap);
+
+	return PTR_ERR_OR_ZERO(priv->port);
+}
+
+static int tusb320_probe(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tusb320_priv *priv;
+	int ret;
+
+	priv = xzalloc(sizeof(*priv));
+
+	priv->dev = &client->dev;
+	i2c_set_clientdata(client, priv);
+
+	priv->regmap = regmap_init_i2c(client, &tusb320_regmap_config);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	ret = tusb320_typec_probe(client, priv);
+	if (ret)
+		return ret;
+
+	/* update initial state */
+	tusb320_state_update_handler(priv, true);
+
+	return ret;
+}
+
+static const struct of_device_id tusb320_typec_dt_match[] = {
+	{ .compatible = "ti,tusb320" },
+	{ .compatible = "ti,tusb320l" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tusb320_typec_dt_match);
+
+static struct driver tusb320_typec_driver = {
+	.name		= "typec-tusb320",
+	.of_match_table = tusb320_typec_dt_match,
+	.probe		= tusb320_probe,
+};
+device_i2c_driver(tusb320_typec_driver);
+
+MODULE_AUTHOR("Michael Auchter <michael.auchter@xxxxxx>");
+MODULE_DESCRIPTION("TI TUSB320 Type-C driver");
+MODULE_LICENSE("GPL v2");
-- 
2.39.2





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux