usb: misc: usb3503: support usb3803 and bypass mode
authorEmanuele Ghidoli <emanuele.ghidoli@toradex.com>
Mon, 13 Mar 2023 16:50:39 +0000 (17:50 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 16 Mar 2023 11:18:03 +0000 (12:18 +0100)
Add support for USB3803 and bypass mode, with this change
is also possible to move the component out of bypass mode.

In bypass mode the downstream port 3 is connected to the
upstream port with low switch resistance R_on.

Controlling mode of operations:

| RESET_N | BYPASS_N | Mode    |
--------------------------------
|    0    |    0     | standby |
|    1    |    0     | bypass  |
|    1    |    1     | hub     |

Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/DataSheets/00001691D.pdf
Signed-off-by: Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com>
Link: https://lore.kernel.org/r/20230313165039.255579-4-francesco@dolcini.it
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/misc/usb3503.c
include/linux/platform_data/usb3503.h

index 3044db9..c6cfd1e 100644 (file)
@@ -46,6 +46,7 @@ struct usb3503 {
        struct device           *dev;
        struct clk              *clk;
        u8      port_off_mask;
+       struct gpio_desc        *bypass;
        struct gpio_desc        *intn;
        struct gpio_desc        *reset;
        struct gpio_desc        *connect;
@@ -109,18 +110,25 @@ static int usb3503_connect(struct usb3503 *hub)
 static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
 {
        struct device *dev = hub->dev;
-       int rst, conn;
+       int rst, bypass, conn;
 
        switch (mode) {
        case USB3503_MODE_HUB:
                conn = 1;
                rst = 0;
+               bypass = 0;
                break;
        case USB3503_MODE_STANDBY:
                conn = 0;
                rst = 1;
+               bypass = 1;
                dev_info(dev, "switched to STANDBY mode\n");
                break;
+       case USB3503_MODE_BYPASS:
+               conn = 0;
+               rst = 0;
+               bypass = 1;
+               break;
        default:
                dev_err(dev, "unknown mode is requested\n");
                return -EINVAL;
@@ -132,6 +140,9 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
        if (hub->reset)
                gpiod_set_value_cansleep(hub->reset, rst);
 
+       if (hub->bypass)
+               gpiod_set_value_cansleep(hub->bypass, bypass);
+
        if (conn) {
                /* Wait T_HUBINIT == 4ms for hub logic to stabilize */
                usleep_range(4000, 10000);
@@ -247,6 +258,14 @@ static int usb3503_probe(struct usb3503 *hub)
        if (hub->connect)
                gpiod_set_consumer_name(hub->connect, "usb3503 connect");
 
+       hub->bypass = devm_gpiod_get_optional(dev, "bypass", GPIOD_OUT_HIGH);
+       if (IS_ERR(hub->bypass)) {
+               err = PTR_ERR(hub->bypass);
+               goto err_clk;
+       }
+       if (hub->bypass)
+               gpiod_set_consumer_name(hub->bypass, "usb3503 bypass");
+
        hub->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
        if (IS_ERR(hub->reset)) {
                err = PTR_ERR(hub->reset);
@@ -382,6 +401,7 @@ MODULE_DEVICE_TABLE(i2c, usb3503_id);
 static const struct of_device_id usb3503_of_match[] = {
        { .compatible = "smsc,usb3503", },
        { .compatible = "smsc,usb3503a", },
+       { .compatible = "smsc,usb3803", },
        {},
 };
 MODULE_DEVICE_TABLE(of, usb3503_of_match);
index d01ef97..f3c942f 100644 (file)
@@ -12,6 +12,7 @@ enum usb3503_mode {
        USB3503_MODE_UNKNOWN,
        USB3503_MODE_HUB,
        USB3503_MODE_STANDBY,
+       USB3503_MODE_BYPASS,
 };
 
 struct usb3503_platform_data {