Skip to content

Commit

Permalink
phy: rockchip-inno-usb3: workaround for USB3 PHY disconnection det issue
Browse files Browse the repository at this point in the history
The rk322xh USB3 PHY has a problem to detect disconnection,
it loses the ability to detect an absence of a far-end
receiver termination specified in USB3 spec Table 6-21,
and this causes the linkstate to change between SS.Inactive
and Polling state, but not return to correct state Rx.detect.

To workaround this bug, we depends on the hub_event to
detect the port linkstate change and do soft disconnect.
And then do USB3 PHY reset and reinit HCD to recovery
the whole USB3.

The workaround process is:
Plug out USB3 device -> hub_event detect PLC and find
USB 3.0 port in the Inactive -> call usb_remove_device()
to do soft disconnect -> call usb_phy_notify_connect()
-> send notifier to DWC3 controller driver to do USB3
PHY reset and reinit HCD.

Change-Id: Icb975581c6fbbb34a7da90ddca47e04a46e5da48
Signed-off-by: William Wu <[email protected]>
  • Loading branch information
wuliangfeng authored and rkhuangtao committed Feb 17, 2017
1 parent 1c6c74e commit 020b3e1
Showing 1 changed file with 46 additions and 0 deletions.
46 changes: 46 additions & 0 deletions drivers/phy/phy-rockchip-inno-usb3.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/usb/phy.h>

#define U3PHY_PORT_NUM 2
#define U3PHY_MAX_CLKS 4
Expand Down Expand Up @@ -140,6 +141,7 @@ struct rockchip_u3phy {
struct rockchip_u3phy_apbcfg apbcfg;
const struct rockchip_u3phy_cfg *cfgs;
struct rockchip_u3phy_port ports[U3PHY_PORT_NUM];
struct usb_phy usb_phy;
};

static inline int param_write(void __iomem *base,
Expand Down Expand Up @@ -650,6 +652,43 @@ static int rockchip_u3phy_port_init(struct rockchip_u3phy *u3phy,
return 0;
}

static int rockchip_u3phy_on_init(struct usb_phy *usb_phy)
{
struct rockchip_u3phy *u3phy =
container_of(usb_phy, struct rockchip_u3phy, usb_phy);

reset_control_deassert(u3phy->rsts[U3_POR_RSTN]);
usleep_range(250, 300);
reset_control_deassert(u3phy->rsts[PIPE_MAC_RSTN]);

return 0;
}

static void rockchip_u3phy_on_shutdown(struct usb_phy *usb_phy)
{
struct rockchip_u3phy *u3phy =
container_of(usb_phy, struct rockchip_u3phy, usb_phy);

reset_control_assert(u3phy->rsts[U3_POR_RSTN]);
reset_control_assert(u3phy->rsts[PIPE_MAC_RSTN]);
usleep_range(15, 20);
}

static int rockchip_u3phy_on_disconnect(struct usb_phy *usb_phy,
enum usb_device_speed speed)
{
struct rockchip_u3phy *u3phy =
container_of(usb_phy, struct rockchip_u3phy, usb_phy);

dev_info(u3phy->dev, "%s device has disconnected\n",
(speed == USB_SPEED_SUPER) ? "U3" : "UW/U2/U1.1/U1");

if (speed == USB_SPEED_SUPER)
atomic_notifier_call_chain(&usb_phy->notifier, 0, NULL);

return 0;
}

static int rockchip_u3phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
Expand Down Expand Up @@ -742,6 +781,13 @@ static int rockchip_u3phy_probe(struct platform_device *pdev)
rockchip_u3phy_rest_deassert(u3phy, U3PHY_MAC_RST);
rockchip_u3phy_clk_disable(u3phy);

u3phy->usb_phy.dev = dev;
u3phy->usb_phy.init = rockchip_u3phy_on_init;
u3phy->usb_phy.shutdown = rockchip_u3phy_on_shutdown;
u3phy->usb_phy.notify_disconnect = rockchip_u3phy_on_disconnect;
usb_add_phy(&u3phy->usb_phy, USB_PHY_TYPE_USB3);
ATOMIC_INIT_NOTIFIER_HEAD(&u3phy->usb_phy.notifier);

dev_info(dev, "Rockchip u3phy initialized successfully\n");
return 0;

Expand Down

0 comments on commit 020b3e1

Please sign in to comment.