* [PATCH v2 1/2] usb: add basic USB Type-C framework
@ 2023-06-21 9:03 Ahmad Fatoum
2023-06-21 9:03 ` [PATCH v2 2/2] usb: add basic TUSB320 Type-C controller support Ahmad Fatoum
2023-06-21 9:16 ` [PATCH v2 1/2] usb: add basic USB Type-C framework Sascha Hauer
0 siblings, 2 replies; 3+ messages in thread
From: Ahmad Fatoum @ 2023-06-21 9:03 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Board code or user scripts may want to base their decisions on the
state of a USB-C connector:
- Is the USB role that of a USB device or of a USB host?
- Is the board being powered externally?
- Is a debug accessory attached?
For this reason, we add here a very simplified Type-C driver core inspired
by Linux that just registers a device and populates it with three parameters:
- $dev.usb_role = { device, host }
- $dev.pwr_role = { sink, source }
- $dev.accessory = { none, audio, debug }
These variables are updated by struct typec_operations::poll, which
corresponds to the IRQ handler for the Linux drivers. The poll function
is called on every variable access and early exits if no IRQ was
indicated.
In future, we may elect to register the poll function with the poller
framework, so it's called cyclically, like we do for e.g. Ethernet link
detection, but for now, this is probably sufficient.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
v1 -> v2:
- add missing <linux/usb/role.h> header from fixup
- split framework from driver
---
Documentation/user/usb.rst | 12 ++
drivers/usb/Kconfig | 2 +
drivers/usb/Makefile | 1 +
drivers/usb/typec/Kconfig | 6 +
drivers/usb/typec/Makefile | 2 +
drivers/usb/typec/class.c | 179 ++++++++++++++++++++++++++++++
include/linux/usb/role.h | 12 ++
include/linux/usb/typec.h | 54 +++++++++
include/linux/usb/typec_altmode.h | 44 ++++++++
9 files changed, 312 insertions(+)
create mode 100644 drivers/usb/typec/Kconfig
create mode 100644 drivers/usb/typec/Makefile
create mode 100644 drivers/usb/typec/class.c
create mode 100644 include/linux/usb/role.h
create mode 100644 include/linux/usb/typec.h
create mode 100644 include/linux/usb/typec_altmode.h
diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
index f2f57ead98d4..20e223da93b5 100644
--- a/Documentation/user/usb.rst
+++ b/Documentation/user/usb.rst
@@ -247,6 +247,18 @@ mode. Once a specific mode has been selected it can't be changed later anymore.
musb-hdrc: 28/31 max ep, 16384/16384 memory
barebox:/
+USB Type-C support
+------------------
+
+barebox can usually stay oblivious to the type of connector used. Sometimes though,
+board code and user scripts may want to base their decisions on how a USB-C connector
+is connected. Type C drivers can thus register with the Type C driver core to
+export a number of device parameters:
+
+- ``$typec0.usb_role`` = { ``none``, ``device``, ``host`` }
+- ``$typec0.pwr_role`` = { ``sink``, ``source`` }
+- ``$typec0.accessory`` = { ``none``, ``audio``, ``debug`` }
+
USB Gadget autostart Support
----------------------------
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index e43c28113f41..d66a75635d46 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -24,6 +24,8 @@ source "drivers/usb/misc/Kconfig"
endif
+source "drivers/usb/typec/Kconfig"
+
source "drivers/usb/gadget/Kconfig"
source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8f1557d5d497..0cac50c0f39b 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_USB_STORAGE) += storage/
obj-y += host/
obj-y += otg/
obj-y += gadget/
+obj-y += typec/
obj-$(CONFIG_USB) += misc/
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
new file mode 100644
index 000000000000..56f10e6ca549
--- /dev/null
+++ b/drivers/usb/typec/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+
+config TYPEC
+ prompt "Compile USB Type-C framework support" if COMPILE_TEST
+ bool
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
new file mode 100644
index 000000000000..6b8347f10c06
--- /dev/null
+++ b/drivers/usb/typec/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_TYPEC) += class.o
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
new file mode 100644
index 000000000000..7f498550f80e
--- /dev/null
+++ b/drivers/usb/typec/class.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <module.h>
+#include <driver.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
+#include <param.h>
+
+enum typec_param_accessory {
+ TYPEC_PARAM_ACCESSORY_NONE,
+ TYPEC_PARAM_ACCESSORY_AUDIO,
+ TYPEC_PARAM_ACCESSORY_DEBUG,
+};
+
+struct typec_port {
+ struct device dev;
+ const struct typec_operations *ops;
+ int pwr_role; /* enum typec_role */
+ int usb_role; /* enum usb_role role */
+ int accessory; /* enum typec_param_accessory */
+};
+
+/**
+ * typec_set_pwr_role - Report power role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report power role changes.
+ */
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
+{
+ port->pwr_role = role;
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_role);
+
+static inline enum typec_param_accessory typec_mode_to_accessory(int mode)
+{
+ switch (mode) {
+ case TYPEC_MODE_AUDIO:
+ return TYPEC_PARAM_ACCESSORY_AUDIO;
+ case TYPEC_MODE_DEBUG:
+ return TYPEC_PARAM_ACCESSORY_DEBUG;
+ default:
+ return TYPEC_PARAM_ACCESSORY_NONE;
+ }
+}
+
+/**
+ * typec_set_mode - Set mode of operation for USB Type-C connector
+ * @port: USB Type-C connector
+ * @mode: Accessory Mode, USB Operation or Safe State
+ *
+ * Configure @port for Accessory Mode @mode. This function will configure the
+ * muxes needed for @mode.
+ */
+int typec_set_mode(struct typec_port *port, int mode)
+{
+ port->accessory = typec_mode_to_accessory(mode);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_mode);
+
+/**
+ * typec_set_role - Set USB role for a Type-C connector
+ * @port: USB Type-C connector
+ * @role: USB role to be switched to
+ *
+ * Set USB role @role for @sw. This is equivalent to Linux
+ * usb_role_switch_set_role();
+ */
+int typec_set_role(struct typec_port *port, enum usb_role role)
+{
+ port->usb_role = role;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_set_role);
+
+/**
+ * typec_get_drvdata - Return private driver data pointer
+ * @port: USB Type-C port
+ */
+void *typec_get_drvdata(struct typec_port *port)
+{
+ return port->dev.priv;
+}
+EXPORT_SYMBOL_GPL(typec_get_drvdata);
+
+static int typec_register_port_dev(struct typec_port *port, const char *name, int id)
+{
+ port->dev.id = id;
+ dev_set_name(&port->dev, name);
+
+ return register_device(&port->dev);
+}
+
+static const char * const usb_role_names[] = {
+ [USB_ROLE_NONE] = "none",
+ [USB_ROLE_HOST] = "host",
+ [USB_ROLE_DEVICE] = "device",
+};
+
+static const char * const pwr_role_names[] = {
+ [TYPEC_SINK] = "sink",
+ [TYPEC_SOURCE] = "source",
+};
+
+static const char * const accessory_names[] = {
+ [TYPEC_PARAM_ACCESSORY_NONE] = "none",
+ [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */
+ [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug",
+};
+
+static int typec_param_update(struct param_d *p, void *priv)
+{
+ struct typec_port *port = priv;
+
+ return port->ops->poll(port);
+}
+
+/**
+ * typec_register_port - Register a USB Type-C Port
+ * @parent: Parent device
+ * @cap: Description of the port
+ *
+ * Registers a device for USB Type-C Port described in @cap.
+ *
+ * Returns handle to the port on success or ERR_PTR on failure.
+ */
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap)
+{
+ struct typec_port *port;
+ struct device *dev;
+ const char *alias;
+ int ret;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ port->ops = cap->ops;
+ dev = &port->dev;
+ dev->parent = parent;
+ dev->of_node = cap->of_node;
+ dev->priv = cap->driver_data;
+
+ alias = dev->of_node ? of_alias_get(dev->of_node) : NULL;
+ if (alias)
+ ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE);
+ if (!alias || ret)
+ ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ of_platform_device_dummy_drv(dev);
+ if (dev->of_node)
+ dev->of_node->dev = dev;
+
+ dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update,
+ &port->usb_role, usb_role_names,
+ ARRAY_SIZE(usb_role_names), port);
+ dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update,
+ &port->pwr_role, pwr_role_names,
+ ARRAY_SIZE(pwr_role_names), port);
+ dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update,
+ &port->accessory, accessory_names,
+ ARRAY_SIZE(accessory_names), port);
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
new file mode 100644
index 000000000000..bf78db7e6fa8
--- /dev/null
+++ b/include/linux/usb/role.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef __LINUX_USB_ROLE_H
+#define __LINUX_USB_ROLE_H
+
+enum usb_role {
+ USB_ROLE_NONE,
+ USB_ROLE_HOST,
+ USB_ROLE_DEVICE,
+};
+
+#endif /* __LINUX_USB_ROLE_H */
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
new file mode 100644
index 000000000000..315dee95e47f
--- /dev/null
+++ b/include/linux/usb/typec.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_USB_TYPEC_H
+#define __LINUX_USB_TYPEC_H
+
+#include <linux/types.h>
+#include <linux/usb/role.h>
+
+struct typec_port;
+
+struct device;
+struct device_node;
+
+enum typec_role {
+ TYPEC_SINK,
+ TYPEC_SOURCE,
+};
+
+enum typec_accessory {
+ TYPEC_ACCESSORY_NONE,
+ TYPEC_ACCESSORY_AUDIO,
+ TYPEC_ACCESSORY_DEBUG,
+};
+
+struct typec_operations {
+ int (*poll)(struct typec_port *port);
+};
+
+/*
+ * struct typec_capability - USB Type-C Port Capabilities
+ * @driver_data: Private pointer for driver specific info
+ * @ops: Port operations vector
+ *
+ * Static capabilities of a single USB Type-C port.
+ */
+struct typec_capability {
+ void *driver_data;
+
+ const struct typec_operations *ops;
+ struct device_node *of_node;
+};
+
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap);
+
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role);
+
+int typec_set_mode(struct typec_port *port, int mode);
+
+int typec_set_role(struct typec_port *port, enum usb_role role);
+
+void *typec_get_drvdata(struct typec_port *port);
+
+#endif /* __LINUX_USB_TYPEC_H */
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
new file mode 100644
index 000000000000..ffa4a8f75420
--- /dev/null
+++ b/include/linux/usb/typec_altmode.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __USB_TYPEC_ALTMODE_H
+#define __USB_TYPEC_ALTMODE_H
+
+/*
+ * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C
+ * Specification. SVID specific connector states are expected to follow and
+ * start from the value TYPEC_STATE_MODAL.
+ */
+enum {
+ TYPEC_STATE_SAFE, /* USB Safe State */
+ TYPEC_STATE_USB, /* USB Operation */
+ TYPEC_STATE_MODAL, /* Alternate Modes */
+};
+
+/*
+ * For the muxes there is no difference between Accessory Modes and Alternate
+ * Modes, so the Accessory Modes are supplied with specific modal state values
+ * here. Unlike with Alternate Modes, where the mux will be linked with the
+ * alternate mode device, the mux for Accessory Modes will be linked with the
+ * port device instead.
+ *
+ * Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode
+ * value for typec_set_mode() when accessory modes are supported.
+ *
+ * USB4 also requires that the pins on the connector are repurposed, just like
+ * Alternate Modes. USB4 mode is however not entered with the Enter Mode Command
+ * like the Alternate Modes are, but instead with a special Enter_USB Message.
+ * The Enter_USB Message can also be used for setting to connector to operate in
+ * USB 3.2 or in USB 2.0 mode instead of USB4.
+ *
+ * The Enter_USB specific "USB Modes" are also supplied here as special modal
+ * state values, just like the Accessory Modes.
+ */
+enum {
+ TYPEC_MODE_USB2 = TYPEC_STATE_MODAL, /* USB 2.0 mode */
+ TYPEC_MODE_USB3, /* USB 3.2 mode */
+ TYPEC_MODE_USB4, /* USB4 mode */
+ TYPEC_MODE_AUDIO, /* Audio Accessory */
+ TYPEC_MODE_DEBUG, /* Debug Accessory */
+};
+
+#endif /* __USB_TYPEC_ALTMODE_H */
--
2.39.2
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2 2/2] usb: add basic TUSB320 Type-C controller support
2023-06-21 9:03 [PATCH v2 1/2] usb: add basic USB Type-C framework Ahmad Fatoum
@ 2023-06-21 9:03 ` Ahmad Fatoum
2023-06-21 9:16 ` [PATCH v2 1/2] usb: add basic USB Type-C framework Sascha Hauer
1 sibling, 0 replies; 3+ messages in thread
From: Ahmad Fatoum @ 2023-06-21 9:03 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
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@pengutronix.de>
---
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@ni.com>
+ */
+
+#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, ®8);
+ 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, ®);
+ 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@ni.com>");
+MODULE_DESCRIPTION("TI TUSB320 Type-C driver");
+MODULE_LICENSE("GPL v2");
--
2.39.2
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2 1/2] usb: add basic USB Type-C framework
2023-06-21 9:03 [PATCH v2 1/2] usb: add basic USB Type-C framework Ahmad Fatoum
2023-06-21 9:03 ` [PATCH v2 2/2] usb: add basic TUSB320 Type-C controller support Ahmad Fatoum
@ 2023-06-21 9:16 ` Sascha Hauer
1 sibling, 0 replies; 3+ messages in thread
From: Sascha Hauer @ 2023-06-21 9:16 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: barebox
On Wed, Jun 21, 2023 at 11:03:29AM +0200, Ahmad Fatoum wrote:
> Board code or user scripts may want to base their decisions on the
> state of a USB-C connector:
>
> - Is the USB role that of a USB device or of a USB host?
> - Is the board being powered externally?
> - Is a debug accessory attached?
>
> For this reason, we add here a very simplified Type-C driver core inspired
> by Linux that just registers a device and populates it with three parameters:
>
> - $dev.usb_role = { device, host }
> - $dev.pwr_role = { sink, source }
> - $dev.accessory = { none, audio, debug }
>
> These variables are updated by struct typec_operations::poll, which
> corresponds to the IRQ handler for the Linux drivers. The poll function
> is called on every variable access and early exits if no IRQ was
> indicated.
>
> In future, we may elect to register the poll function with the poller
> framework, so it's called cyclically, like we do for e.g. Ethernet link
> detection, but for now, this is probably sufficient.
>
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
> v1 -> v2:
> - add missing <linux/usb/role.h> header from fixup
> - split framework from driver
Applied, thanks
Sascha
> ---
> Documentation/user/usb.rst | 12 ++
> drivers/usb/Kconfig | 2 +
> drivers/usb/Makefile | 1 +
> drivers/usb/typec/Kconfig | 6 +
> drivers/usb/typec/Makefile | 2 +
> drivers/usb/typec/class.c | 179 ++++++++++++++++++++++++++++++
> include/linux/usb/role.h | 12 ++
> include/linux/usb/typec.h | 54 +++++++++
> include/linux/usb/typec_altmode.h | 44 ++++++++
> 9 files changed, 312 insertions(+)
> create mode 100644 drivers/usb/typec/Kconfig
> create mode 100644 drivers/usb/typec/Makefile
> create mode 100644 drivers/usb/typec/class.c
> create mode 100644 include/linux/usb/role.h
> create mode 100644 include/linux/usb/typec.h
> create mode 100644 include/linux/usb/typec_altmode.h
>
> diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
> index f2f57ead98d4..20e223da93b5 100644
> --- a/Documentation/user/usb.rst
> +++ b/Documentation/user/usb.rst
> @@ -247,6 +247,18 @@ mode. Once a specific mode has been selected it can't be changed later anymore.
> musb-hdrc: 28/31 max ep, 16384/16384 memory
> barebox:/
>
> +USB Type-C support
> +------------------
> +
> +barebox can usually stay oblivious to the type of connector used. Sometimes though,
> +board code and user scripts may want to base their decisions on how a USB-C connector
> +is connected. Type C drivers can thus register with the Type C driver core to
> +export a number of device parameters:
> +
> +- ``$typec0.usb_role`` = { ``none``, ``device``, ``host`` }
> +- ``$typec0.pwr_role`` = { ``sink``, ``source`` }
> +- ``$typec0.accessory`` = { ``none``, ``audio``, ``debug`` }
> +
> USB Gadget autostart Support
> ----------------------------
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index e43c28113f41..d66a75635d46 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -24,6 +24,8 @@ source "drivers/usb/misc/Kconfig"
>
> endif
>
> +source "drivers/usb/typec/Kconfig"
> +
> source "drivers/usb/gadget/Kconfig"
>
> source "drivers/usb/musb/Kconfig"
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index 8f1557d5d497..0cac50c0f39b 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -8,5 +8,6 @@ obj-$(CONFIG_USB_STORAGE) += storage/
> obj-y += host/
> obj-y += otg/
> obj-y += gadget/
> +obj-y += typec/
> obj-$(CONFIG_USB) += misc/
>
> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> new file mode 100644
> index 000000000000..56f10e6ca549
> --- /dev/null
> +++ b/drivers/usb/typec/Kconfig
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +
> +config TYPEC
> + prompt "Compile USB Type-C framework support" if COMPILE_TEST
> + bool
> diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
> new file mode 100644
> index 000000000000..6b8347f10c06
> --- /dev/null
> +++ b/drivers/usb/typec/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_TYPEC) += class.o
> diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
> new file mode 100644
> index 000000000000..7f498550f80e
> --- /dev/null
> +++ b/drivers/usb/typec/class.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USB Type-C Connector Class
> + *
> + * Copyright (C) 2017, Intel Corporation
> + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> + */
> +
> +#include <module.h>
> +#include <driver.h>
> +#include <linux/usb/role.h>
> +#include <linux/usb/typec.h>
> +#include <linux/usb/typec_altmode.h>
> +#include <param.h>
> +
> +enum typec_param_accessory {
> + TYPEC_PARAM_ACCESSORY_NONE,
> + TYPEC_PARAM_ACCESSORY_AUDIO,
> + TYPEC_PARAM_ACCESSORY_DEBUG,
> +};
> +
> +struct typec_port {
> + struct device dev;
> + const struct typec_operations *ops;
> + int pwr_role; /* enum typec_role */
> + int usb_role; /* enum usb_role role */
> + int accessory; /* enum typec_param_accessory */
> +};
> +
> +/**
> + * typec_set_pwr_role - Report power role change
> + * @port: The USB Type-C Port where the role was changed
> + * @role: The new data role
> + *
> + * This routine is used by the port drivers to report power role changes.
> + */
> +void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
> +{
> + port->pwr_role = role;
> +}
> +EXPORT_SYMBOL_GPL(typec_set_pwr_role);
> +
> +static inline enum typec_param_accessory typec_mode_to_accessory(int mode)
> +{
> + switch (mode) {
> + case TYPEC_MODE_AUDIO:
> + return TYPEC_PARAM_ACCESSORY_AUDIO;
> + case TYPEC_MODE_DEBUG:
> + return TYPEC_PARAM_ACCESSORY_DEBUG;
> + default:
> + return TYPEC_PARAM_ACCESSORY_NONE;
> + }
> +}
> +
> +/**
> + * typec_set_mode - Set mode of operation for USB Type-C connector
> + * @port: USB Type-C connector
> + * @mode: Accessory Mode, USB Operation or Safe State
> + *
> + * Configure @port for Accessory Mode @mode. This function will configure the
> + * muxes needed for @mode.
> + */
> +int typec_set_mode(struct typec_port *port, int mode)
> +{
> + port->accessory = typec_mode_to_accessory(mode);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(typec_set_mode);
> +
> +/**
> + * typec_set_role - Set USB role for a Type-C connector
> + * @port: USB Type-C connector
> + * @role: USB role to be switched to
> + *
> + * Set USB role @role for @sw. This is equivalent to Linux
> + * usb_role_switch_set_role();
> + */
> +int typec_set_role(struct typec_port *port, enum usb_role role)
> +{
> + port->usb_role = role;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(typec_set_role);
> +
> +/**
> + * typec_get_drvdata - Return private driver data pointer
> + * @port: USB Type-C port
> + */
> +void *typec_get_drvdata(struct typec_port *port)
> +{
> + return port->dev.priv;
> +}
> +EXPORT_SYMBOL_GPL(typec_get_drvdata);
> +
> +static int typec_register_port_dev(struct typec_port *port, const char *name, int id)
> +{
> + port->dev.id = id;
> + dev_set_name(&port->dev, name);
> +
> + return register_device(&port->dev);
> +}
> +
> +static const char * const usb_role_names[] = {
> + [USB_ROLE_NONE] = "none",
> + [USB_ROLE_HOST] = "host",
> + [USB_ROLE_DEVICE] = "device",
> +};
> +
> +static const char * const pwr_role_names[] = {
> + [TYPEC_SINK] = "sink",
> + [TYPEC_SOURCE] = "source",
> +};
> +
> +static const char * const accessory_names[] = {
> + [TYPEC_PARAM_ACCESSORY_NONE] = "none",
> + [TYPEC_PARAM_ACCESSORY_AUDIO] = "audio", /* analog */
> + [TYPEC_PARAM_ACCESSORY_DEBUG] = "debug",
> +};
> +
> +static int typec_param_update(struct param_d *p, void *priv)
> +{
> + struct typec_port *port = priv;
> +
> + return port->ops->poll(port);
> +}
> +
> +/**
> + * typec_register_port - Register a USB Type-C Port
> + * @parent: Parent device
> + * @cap: Description of the port
> + *
> + * Registers a device for USB Type-C Port described in @cap.
> + *
> + * Returns handle to the port on success or ERR_PTR on failure.
> + */
> +struct typec_port *typec_register_port(struct device *parent,
> + const struct typec_capability *cap)
> +{
> + struct typec_port *port;
> + struct device *dev;
> + const char *alias;
> + int ret;
> +
> + port = kzalloc(sizeof(*port), GFP_KERNEL);
> + if (!port)
> + return ERR_PTR(-ENOMEM);
> +
> + port->ops = cap->ops;
> + dev = &port->dev;
> + dev->parent = parent;
> + dev->of_node = cap->of_node;
> + dev->priv = cap->driver_data;
> +
> + alias = dev->of_node ? of_alias_get(dev->of_node) : NULL;
> + if (alias)
> + ret = typec_register_port_dev(port, alias, DEVICE_ID_SINGLE);
> + if (!alias || ret)
> + ret = typec_register_port_dev(port, "typec", DEVICE_ID_DYNAMIC);
> +
> + if (ret)
> + return ERR_PTR(ret);
> +
> + of_platform_device_dummy_drv(dev);
> + if (dev->of_node)
> + dev->of_node->dev = dev;
> +
> + dev_add_param_enum(dev, "usb_role", param_set_readonly, typec_param_update,
> + &port->usb_role, usb_role_names,
> + ARRAY_SIZE(usb_role_names), port);
> + dev_add_param_enum(dev, "pwr_role", param_set_readonly, typec_param_update,
> + &port->pwr_role, pwr_role_names,
> + ARRAY_SIZE(pwr_role_names), port);
> + dev_add_param_enum(dev, "accessory", param_set_readonly, typec_param_update,
> + &port->accessory, accessory_names,
> + ARRAY_SIZE(accessory_names), port);
> +
> + return port;
> +}
> +EXPORT_SYMBOL_GPL(typec_register_port);
> diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
> new file mode 100644
> index 000000000000..bf78db7e6fa8
> --- /dev/null
> +++ b/include/linux/usb/role.h
> @@ -0,0 +1,12 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#ifndef __LINUX_USB_ROLE_H
> +#define __LINUX_USB_ROLE_H
> +
> +enum usb_role {
> + USB_ROLE_NONE,
> + USB_ROLE_HOST,
> + USB_ROLE_DEVICE,
> +};
> +
> +#endif /* __LINUX_USB_ROLE_H */
> diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> new file mode 100644
> index 000000000000..315dee95e47f
> --- /dev/null
> +++ b/include/linux/usb/typec.h
> @@ -0,0 +1,54 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __LINUX_USB_TYPEC_H
> +#define __LINUX_USB_TYPEC_H
> +
> +#include <linux/types.h>
> +#include <linux/usb/role.h>
> +
> +struct typec_port;
> +
> +struct device;
> +struct device_node;
> +
> +enum typec_role {
> + TYPEC_SINK,
> + TYPEC_SOURCE,
> +};
> +
> +enum typec_accessory {
> + TYPEC_ACCESSORY_NONE,
> + TYPEC_ACCESSORY_AUDIO,
> + TYPEC_ACCESSORY_DEBUG,
> +};
> +
> +struct typec_operations {
> + int (*poll)(struct typec_port *port);
> +};
> +
> +/*
> + * struct typec_capability - USB Type-C Port Capabilities
> + * @driver_data: Private pointer for driver specific info
> + * @ops: Port operations vector
> + *
> + * Static capabilities of a single USB Type-C port.
> + */
> +struct typec_capability {
> + void *driver_data;
> +
> + const struct typec_operations *ops;
> + struct device_node *of_node;
> +};
> +
> +struct typec_port *typec_register_port(struct device *parent,
> + const struct typec_capability *cap);
> +
> +void typec_set_pwr_role(struct typec_port *port, enum typec_role role);
> +
> +int typec_set_mode(struct typec_port *port, int mode);
> +
> +int typec_set_role(struct typec_port *port, enum usb_role role);
> +
> +void *typec_get_drvdata(struct typec_port *port);
> +
> +#endif /* __LINUX_USB_TYPEC_H */
> diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
> new file mode 100644
> index 000000000000..ffa4a8f75420
> --- /dev/null
> +++ b/include/linux/usb/typec_altmode.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __USB_TYPEC_ALTMODE_H
> +#define __USB_TYPEC_ALTMODE_H
> +
> +/*
> + * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C
> + * Specification. SVID specific connector states are expected to follow and
> + * start from the value TYPEC_STATE_MODAL.
> + */
> +enum {
> + TYPEC_STATE_SAFE, /* USB Safe State */
> + TYPEC_STATE_USB, /* USB Operation */
> + TYPEC_STATE_MODAL, /* Alternate Modes */
> +};
> +
> +/*
> + * For the muxes there is no difference between Accessory Modes and Alternate
> + * Modes, so the Accessory Modes are supplied with specific modal state values
> + * here. Unlike with Alternate Modes, where the mux will be linked with the
> + * alternate mode device, the mux for Accessory Modes will be linked with the
> + * port device instead.
> + *
> + * Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode
> + * value for typec_set_mode() when accessory modes are supported.
> + *
> + * USB4 also requires that the pins on the connector are repurposed, just like
> + * Alternate Modes. USB4 mode is however not entered with the Enter Mode Command
> + * like the Alternate Modes are, but instead with a special Enter_USB Message.
> + * The Enter_USB Message can also be used for setting to connector to operate in
> + * USB 3.2 or in USB 2.0 mode instead of USB4.
> + *
> + * The Enter_USB specific "USB Modes" are also supplied here as special modal
> + * state values, just like the Accessory Modes.
> + */
> +enum {
> + TYPEC_MODE_USB2 = TYPEC_STATE_MODAL, /* USB 2.0 mode */
> + TYPEC_MODE_USB3, /* USB 3.2 mode */
> + TYPEC_MODE_USB4, /* USB4 mode */
> + TYPEC_MODE_AUDIO, /* Audio Accessory */
> + TYPEC_MODE_DEBUG, /* Debug Accessory */
> +};
> +
> +#endif /* __USB_TYPEC_ALTMODE_H */
> --
> 2.39.2
>
>
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-06-21 9:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-21 9:03 [PATCH v2 1/2] usb: add basic USB Type-C framework Ahmad Fatoum
2023-06-21 9:03 ` [PATCH v2 2/2] usb: add basic TUSB320 Type-C controller support Ahmad Fatoum
2023-06-21 9:16 ` [PATCH v2 1/2] usb: add basic USB Type-C framework Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox