[PATCH] usb: misc: add support for new OF onboard hub binding

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

 



The new binding allows describing hubs that lacks a control bus (e.g.
I2C) in the DT and thus replaces the ugly hack of specifying USB hub
resets as gpio-hogs. It's already in use for boards we support, like the
STM32MP13/5 Discovery kits and will be useful for the i.MX8MP Debix
board, so port over the driver from Linux.

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
 drivers/usb/core/usb.c             |  2 +
 drivers/usb/misc/Kconfig           | 10 ++++
 drivers/usb/misc/Makefile          |  1 +
 drivers/usb/misc/onboard_usb_hub.c | 86 ++++++++++++++++++++++++++++++
 drivers/usb/misc/onboard_usb_hub.h | 36 +++++++++++++
 include/usb/usb.h                  |  8 +++
 6 files changed, 143 insertions(+)
 create mode 100644 drivers/usb/misc/onboard_usb_hub.c
 create mode 100644 drivers/usb/misc/onboard_usb_hub.h

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 7c589ec06f24..a974614c062e 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -576,6 +576,8 @@ int usb_host_detect(struct usb_host *host)
 {
 	int ret;
 
+	of_usb_host_probe_hubs(host);
+
 	if (!host->root_dev) {
 		if (host->init) {
 			ret = host->init(host);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 9799af47256e..fde57fd74309 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -14,3 +14,13 @@ config USB_HUB_USB251XB
 	  Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration
 	  parameters may be set in devicetree or platform data.
 	  Say Y or M here if you need to configure such a device via SMBus.
+
+config USB_ONBOARD_HUB
+	bool "Onboard USB hub support"
+	depends on OFDEVICE || COMPILE_TEST
+	help
+	  Say Y here if you want to support discrete onboard USB hubs that
+	  don't require an additional control bus for initialization, but
+	  need some non-trivial form of initialization, such as enabling a
+	  power regulator. An example for such a hub is the Realtek
+	  RTS5411.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index be5c044f5a57..e00f66a5ed1c 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -4,3 +4,4 @@
 # (the ones that don't fit into any other categories)
 #
 obj-$(CONFIG_USB_HUB_USB251XB)		+= usb251xb.o
+obj-$(CONFIG_USB_ONBOARD_HUB)		+= onboard_usb_hub.o
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
new file mode 100644
index 000000000000..0b847f06ad45
--- /dev/null
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for onboard USB hubs
+ *
+ * Copyright (c) 2022, Google LLC
+ */
+
+#include <driver.h>
+#include <gpiod.h>
+#include <init.h>
+#include <of.h>
+#include <linux/printk.h>
+#include <of_device.h>
+#include <regulator.h>
+#include <usb/usb.h>
+
+#include "onboard_usb_hub.h"
+
+void of_usb_host_probe_hubs(struct usb_host *host)
+{
+	struct device_node *np;
+
+	np = dev_of_node(host->hw_dev);
+	if (!np)
+		return;
+
+	of_platform_populate(np, onboard_hub_match, host->hw_dev);
+}
+
+struct onboard_hub {
+	struct regulator *vdd;
+	struct device *dev;
+	const struct onboard_hub_pdata *pdata;
+	int reset_gpio;
+};
+
+static int onboard_hub_power_on(struct onboard_hub *hub)
+{
+	int err;
+
+	err = regulator_enable(hub->vdd);
+	if (err) {
+		dev_err(hub->dev, "failed to enable regulator: %pe\n",
+			ERR_PTR(err));
+		return err;
+	}
+
+	udelay(hub->pdata->reset_us);
+	gpiod_set_value(hub->reset_gpio, 0);
+
+	return 0;
+}
+
+static int onboard_hub_probe(struct device *dev)
+{
+	struct onboard_hub *hub;
+
+	hub = xzalloc(sizeof(*hub));
+
+	hub->pdata = device_get_match_data(dev);
+	if (!hub->pdata)
+		return -EINVAL;
+
+	hub->vdd = regulator_get(dev, "vdd");
+	if (IS_ERR(hub->vdd))
+		return PTR_ERR(hub->vdd);
+
+	hub->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (hub->reset_gpio < 0 && hub->reset_gpio != -ENOENT)
+		return dev_err_probe(dev, hub->reset_gpio, "failed to get reset GPIO\n");
+
+	hub->dev = dev;
+
+	return onboard_hub_power_on(hub);
+}
+
+static struct driver onboard_hub_driver = {
+	.name = "onboard-usb-hub",
+	.probe = onboard_hub_probe,
+	.of_compatible = onboard_hub_match,
+};
+device_platform_driver(onboard_hub_driver);
+
+MODULE_AUTHOR("Matthias Kaehlcke <mka@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Driver for discrete onboard USB hubs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h
new file mode 100644
index 000000000000..e90be47b6735
--- /dev/null
+++ b/drivers/usb/misc/onboard_usb_hub.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2022, Google LLC
+ */
+
+#ifndef _USB_MISC_ONBOARD_USB_HUB_H
+#define _USB_MISC_ONBOARD_USB_HUB_H
+
+struct onboard_hub_pdata {
+	unsigned long reset_us;		/* reset pulse width in us */
+};
+
+static const struct onboard_hub_pdata microchip_usb424_data = {
+	.reset_us = 1,
+};
+
+static const struct onboard_hub_pdata realtek_rts5411_data = {
+	.reset_us = 0,
+};
+
+static const struct onboard_hub_pdata ti_tusb8041_data = {
+	.reset_us = 3000,
+};
+
+const struct of_device_id onboard_hub_match[] = {
+	{ .compatible = "usb424,2514", .data = &microchip_usb424_data, },
+	{ .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
+	{ .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+	{ .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
+	{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
+	{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
+	{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
+	{}
+};
+
+#endif /* _USB_MISC_ONBOARD_USB_HUB_H */
diff --git a/include/usb/usb.h b/include/usb/usb.h
index 9b36122436d5..717bcf935a42 100644
--- a/include/usb/usb.h
+++ b/include/usb/usb.h
@@ -484,4 +484,12 @@ extern struct list_head usb_device_list;
 
 bool usb_hub_is_root_hub(struct usb_device *hdev);
 
+#ifdef CONFIG_USB_ONBOARD_HUB
+void of_usb_host_probe_hubs(struct usb_host *host);
+#else
+static inline void of_usb_host_probe_hubs(struct usb_host *host)
+{
+}
+#endif
+
 #endif /*_USB_H_ */
-- 
2.30.2





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

  Powered by Linux