[PATCH 2/2] usb: misc: usb5744: Add support for USB hub controller

From: Michal Simek
Date: Tue Feb 09 2021 - 04:58:09 EST


From: Piyush Mehta <piyush.mehta@xxxxxxxxxx>

This patch adds a USB GPIO based hub reset for USB5744 hub. This usb5744
driver trigger hub reset signal after soft reset or core Reset. The HUB
needs to be resetted after completion of phy initialization. After the
toggling of gpio, hub configure using i2c usb attached command.

USB5744 hub can be used without any I2C connection, is handled by a
simple platform device driver.

As part of the reset, sets the direction of the pin to output before
toggling the pin. Delay of millisecond is added in between low and
high to meet the setup and hold time requirement of the reset.

Signed-off-by: Piyush Mehta <piyush.mehta@xxxxxxxxxx>
Signed-off-by: Michal Simek <michal.simek@xxxxxxxxxx>
---

MAINTAINERS | 1 +
drivers/usb/misc/Kconfig | 9 +++
drivers/usb/misc/Makefile | 1 +
drivers/usb/misc/usb5744.c | 115 +++++++++++++++++++++++++++++++++++++
4 files changed, 126 insertions(+)
create mode 100644 drivers/usb/misc/usb5744.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7439471b5d37..56d1fcdd24f6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2706,6 +2706,7 @@ F: drivers/edac/synopsys_edac.c
F: drivers/i2c/busses/i2c-cadence.c
F: drivers/i2c/busses/i2c-xiic.c
F: drivers/mmc/host/sdhci-of-arasan.c
+F: drivers/usb/misc/usb5744.c
N: zynq
N: xilinx

diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 8f1144359012..30335b5c4f88 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -242,6 +242,15 @@ config USB_HUB_USB251XB
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_USB5744
+ tristate "Microchip USB5744 Hub driver"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ This option enables support for Microchip USB5744 Hub. This driver
+ optionally reset the hub using gpio pin and configure hub via i2c if
+ connected.
+
config USB_HSIC_USB3503
tristate "USB3503 HSIC to USB20 Driver"
depends on I2C
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 5f4e598573ab..5920146a506a 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
obj-$(CONFIG_USB_YUREX) += yurex.o
obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o
+obj-$(CONFIG_USB_USB5744) += usb5744.o
obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o
obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
diff --git a/drivers/usb/misc/usb5744.c b/drivers/usb/misc/usb5744.c
new file mode 100644
index 000000000000..729b76345c69
--- /dev/null
+++ b/drivers/usb/misc/usb5744.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Microchip USB5744 4-port hub.
+ *
+ * Copyright (c) 2021 Xilinx, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+
+static int usb5744_init_hw(struct device *dev)
+{
+ struct gpio_desc *reset_gpio;
+
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio)) {
+ dev_err(dev, "Failed to bind reset gpio");
+ return -ENODEV;
+ }
+
+ if (reset_gpio) {
+ /* Toggle RESET_N to reset the hub. */
+ gpiod_set_value(reset_gpio, 0);
+ usleep_range(5, 20); /* trstia */
+ gpiod_set_value(reset_gpio, 1);
+ usleep_range(5000, 10000); /* tcsh */
+ }
+
+ return 0;
+}
+
+static int usb5744_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ int ret;
+
+ /* Trigger gpio reset to the hub. */
+ ret = usb5744_init_hw(dev);
+ if (ret)
+ return ret;
+
+ /* Send SMBus command to boot hub. */
+ ret = i2c_smbus_write_word_data(client, 0xAA, swab16(0x5600));
+ if (ret < 0)
+ dev_err(dev, "Sending boot command failed");
+
+ return ret;
+}
+
+static int usb5744_platform_probe(struct platform_device *pdev)
+{
+ /* Trigger gpio reset to the hub. */
+ return usb5744_init_hw(&pdev->dev);
+}
+
+static const struct i2c_device_id usb5744_id[] = {
+ { "usb5744", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, usb5744_id);
+
+static struct i2c_driver usb5744_i2c_driver = {
+ .driver = {
+ .name = "usb5744",
+ },
+ .probe = usb5744_i2c_probe,
+ .id_table = usb5744_id,
+};
+
+static const struct of_device_id usb5744_platform_id[] = {
+ { .compatible = "microchip,usb5744", },
+ { }
+};
+
+static struct platform_driver usb5744_platform_driver = {
+ .driver = {
+ .name = "microchip,usb5744",
+ .of_match_table = usb5744_platform_id,
+ },
+ .probe = usb5744_platform_probe,
+};
+
+static int __init usb5744_init(void)
+{
+ int err;
+
+ err = i2c_add_driver(&usb5744_i2c_driver);
+ if (err != 0)
+ pr_err("usb5744: Failed to register I2C driver: %d\n", err);
+
+ err = platform_driver_register(&usb5744_platform_driver);
+ if (err != 0)
+ pr_err("usb5744: Failed to register platform driver: %d\n",
+ err);
+ return 0;
+}
+module_init(usb5744_init);
+
+static void __exit usb5744_exit(void)
+{
+ platform_driver_unregister(&usb5744_platform_driver);
+ i2c_del_driver(&usb5744_i2c_driver);
+}
+module_exit(usb5744_exit);
+
+MODULE_AUTHOR("Piyush Mehta <piyush.mehta@xxxxxxxxxx>");
+MODULE_AUTHOR("Michal Simek <michal.simek@xxxxxxxxxx>");
+MODULE_DESCRIPTION("USB5744 Hub");
+MODULE_LICENSE("GPL v2");
--
2.30.0