usb: host: ehci-platform: Add device_link between the ehci and companion

Rockchip ehci controller has companion ohci controller,
and the ohci depends on the ehci, if the ehci controller
has been suspend or shutdown, it shouldn't access the ohci
controller, otherwise, the system may hang in ohci_readl
or ohci_writel on Rockchip platforms.

In order to enforce suspend/resume and shutdown ordering,
this commit creates link between the ehci and ohci. This
link avoids to suspend or shutdown ehci before its companion
ohci.

Corresponding, DL_FLAG_AUTOREMOVE_CONSUMER can't be added,
then device_link_removed should be added explicitly.

Meanwhile, the ehci must not be resumed after its companion
if the companion is the consumer device of ehci.

Signed-off-by: William Wu <william.wu@rock-chips.com>
Change-Id: If2935c651341917251c2178a6833357a73b71079
This commit is contained in:
William Wu
2022-09-14 10:42:50 +08:00
committed by Tao Huang
parent 4e8841e155
commit 68850661b5
+26 -1
View File
@@ -276,6 +276,8 @@ static int ehci_platform_probe(struct platform_device *dev)
struct ehci_platform_priv *priv;
struct ehci_hcd *ehci;
int err, irq, clk = 0;
struct device *companion_dev;
struct device_link *link;
if (usb_disabled())
return -ENODEV;
@@ -414,6 +416,21 @@ static int ehci_platform_probe(struct platform_device *dev)
if (of_usb_get_phy_mode(dev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC)
ehci_usic_init(hcd);
if (of_device_is_compatible(dev->dev.of_node,
"rockchip,rk3588-ehci")) {
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (companion_dev) {
link = device_link_add(companion_dev, hcd->self.controller,
DL_FLAG_STATELESS);
if (!link) {
dev_err(&dev->dev, "Unable to link %s\n",
dev_name(companion_dev));
err = -EINVAL;
goto err_power;
}
}
}
device_wakeup_enable(hcd->self.controller);
device_enable_async_suspend(hcd->self.controller);
platform_set_drvdata(dev, hcd);
@@ -447,11 +464,19 @@ static int ehci_platform_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
struct device *companion_dev;
int clk;
if (priv->quirk_poll)
quirk_poll_end(priv);
if (of_device_is_compatible(dev->dev.of_node,
"rockchip,rk3588-ehci")) {
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (companion_dev)
device_link_remove(companion_dev, hcd->self.controller);
}
usb_remove_hcd(hcd);
if (pdata->power_off)
@@ -510,7 +535,7 @@ static int __maybe_unused ehci_platform_resume(struct device *dev)
}
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (companion_dev) {
if (companion_dev && !device_is_dependent(hcd->self.controller, companion_dev)) {
device_pm_wait_for_dev(hcd->self.controller, companion_dev);
put_device(companion_dev);
}