[PATCH v1 3/3] usb: misc: usb3503: support usb3803 and bypass mode

From: Francesco Dolcini
Date: Mon Mar 13 2023 - 12:56:53 EST


From: Emanuele Ghidoli <emanuele.ghidoli@xxxxxxxxxxx>

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@xxxxxxxxxxx>
Signed-off-by: Francesco Dolcini <francesco.dolcini@xxxxxxxxxxx>
---
drivers/usb/misc/usb3503.c | 22 +++++++++++++++++++++-
include/linux/platform_data/usb3503.h | 1 +
2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 3044db9fd8aa..c6cfd1edaf76 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -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);
diff --git a/include/linux/platform_data/usb3503.h b/include/linux/platform_data/usb3503.h
index d01ef97ddf36..f3c942f396f8 100644
--- a/include/linux/platform_data/usb3503.h
+++ b/include/linux/platform_data/usb3503.h
@@ -12,6 +12,7 @@ enum usb3503_mode {
USB3503_MODE_UNKNOWN,
USB3503_MODE_HUB,
USB3503_MODE_STANDBY,
+ USB3503_MODE_BYPASS,
};

struct usb3503_platform_data {
--
2.25.1