From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Wed, 21 Jun 2023 11:06:28 +0200 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1qBtnJ-00FT97-CX for lore@lore.pengutronix.de; Wed, 21 Jun 2023 11:06:28 +0200 Received: from bombadil.infradead.org ([2607:7c80:54:3::133]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qBtnG-0002Cu-8e for lore@pengutronix.de; Wed, 21 Jun 2023 11:06:27 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner; bh=kLLUBiiYvTDtJPXYbGLJoN1kJyCgFNxuODt8kDto+zg=; b=44+NGkUpvHmeVckyeWmpSsBrA+ 9ZKT2rYMlHui4Xivuga4oyiA7cpVVsuWfNKrovyzpbpiB4Ssvq20654ZTrwjDanWiqyfRYVJTMFGT V/s0ppTu5a6FEGq04QgNZwdno3BYyoNXvxD/eokZwHn8jVKdLBUKgS9nNIHcij6HtxtjQ2JHSV5WW E00T9qyw/ItWiVO1dRWrFOSVzqPD5V77x217RKZfP7cbyhT3jIs0JIhujkLrSepr/95aRiMbW/Pq6 c/o0DlxSul2czx2yzFP9zIRA89Trw3VOinZZBjGqqVjrl+Ewf1lkVsIRQyiEHd/wKRvCiFjCOir5j g18d3N8A==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1qBtkk-00DmxL-0d; Wed, 21 Jun 2023 09:03:50 +0000 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1qBtkg-00DmwX-27 for barebox@lists.infradead.org; Wed, 21 Jun 2023 09:03:49 +0000 Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qBtke-0001ce-MN; Wed, 21 Jun 2023 11:03:44 +0200 Received: from [2a0a:edc0:0:1101:1d::54] (helo=dude05.red.stw.pengutronix.de) by drehscheibe.grey.stw.pengutronix.de with esmtp (Exim 4.94.2) (envelope-from ) id 1qBtke-008zbO-04; Wed, 21 Jun 2023 11:03:44 +0200 Received: from afa by dude05.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1qBtkd-00BDNB-1R; Wed, 21 Jun 2023 11:03:43 +0200 From: Ahmad Fatoum To: barebox@lists.infradead.org Cc: Ahmad Fatoum Date: Wed, 21 Jun 2023 11:03:29 +0200 Message-Id: <20230621090329.2652672-1-a.fatoum@pengutronix.de> X-Mailer: git-send-email 2.39.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230621_020347_017752_DE5141E4 X-CRM114-Status: GOOD ( 31.73 ) X-BeenThere: barebox@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "barebox" X-SA-Exim-Connect-IP: 2607:7c80:54:3::133 X-SA-Exim-Mail-From: barebox-bounces+lore=pengutronix.de@lists.infradead.org X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on metis.ext.pengutronix.de X-Spam-Level: X-Spam-Status: No, score=-4.9 required=4.0 tests=AWL,BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.2 Subject: [PATCH v2 1/2] usb: add basic USB Type-C framework X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on metis.ext.pengutronix.de) 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 --- v1 -> v2: - add missing 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 + */ + +#include +#include +#include +#include +#include +#include + +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 +#include + +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