Commit b91e6107 authored by Emanuele Ghidoli's avatar Emanuele Ghidoli Committed by Greg Kroah-Hartman
Browse files

usb: misc: usb3503: support usb3803 and bypass mode

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: default avatarEmanuele Ghidoli <emanuele.ghidoli@toradex.com>
Signed-off-by: default avatarFrancesco Dolcini <francesco.dolcini@toradex.com>
Link: https://lore.kernel.org/r/20230313165039.255579-4-francesco@dolcini.it


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b04b32cd
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ enum usb3503_mode {
	USB3503_MODE_UNKNOWN,
	USB3503_MODE_HUB,
	USB3503_MODE_STANDBY,
	USB3503_MODE_BYPASS,
};

struct usb3503_platform_data {