Skip to content

Commit

Permalink
usb: xhci-plat: Add remote wakeup support
Browse files Browse the repository at this point in the history
This patch adds support for enabling remote wakeup capability
to the host controller

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Piyush Mehta <piyush.mehta@xilinx.com>
Signed-off-by: Manish Narani <manish.narani@xilinx.com>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
State: pending
[piyushm squashed with:
usb: dwc3: xilinx: enable D3 power state entry feature
usb: xhci-plat: Add remote wakeup support for xilinx
usb: dwc3: Added remote wake-up in xilinx glue driver
]
  • Loading branch information
Piyush Mehta authored and michalsimek committed Dec 12, 2022
1 parent d05808e commit 22f32ef
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 7 deletions.
12 changes: 12 additions & 0 deletions drivers/usb/dwc3/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,18 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
}

/* Put the core into D3 state */
if (dwc->dwc3_pmu) {
int ret;

ret = regulator_disable(dwc->dwc3_pmu);
if (ret) {
dev_err(dwc->dev,
"Failed to disable dwc3_pmu supply\n");
return ret;
}
}

return 0;
}

Expand Down
4 changes: 4 additions & 0 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <linux/ulpi/interface.h>

#include <linux/phy/phy.h>
#include <../drivers/usb/host/xhci.h>

#include <linux/power_supply.h>

Expand Down Expand Up @@ -1525,6 +1526,9 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);

typedef void (*dwc3_wakeup_t)(struct device *dev, bool wakeup);
void dwc3_host_wakeup_register(dwc3_wakeup_t func);

#define DWC3_IP_IS(_ip) \
(dwc->ip == _ip##_IP)

Expand Down
68 changes: 61 additions & 7 deletions drivers/usb/dwc3/dwc3-xilinx.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include <linux/phy/phy.h>

#include "core.h"

/* USB phy reset mask register */
#define XLNX_USB_PHY_RST_EN 0x001C
#define XLNX_PHY_RST_MASK 0x1
Expand Down Expand Up @@ -80,7 +82,9 @@ struct dwc3_xlnx {
struct regulator *dwc3_pmu;
struct regulator_dev *dwc3_xlnx_reg_rdev;
enum dwc3_xlnx_core_state pmu_state;
bool wakeup_capable;
struct reset_control *crst;
bool enable_d3_suspend;
enum usb_dr_mode dr_mode;
struct regulator_desc dwc3_xlnx_reg_desc;
};
Expand Down Expand Up @@ -118,6 +122,10 @@ static int dwc3_zynqmp_power_req(struct device *dev, bool on)
priv_data = dev_get_drvdata(dev);
reg_base = priv_data->regs;

/* Check if entering into D3 state is allowed during suspend */
if (!priv_data->enable_d3_suspend)
return 0;

if (on) {
dev_dbg(priv_data->dev,
"trying to set power state to D0....\n");
Expand Down Expand Up @@ -154,6 +162,8 @@ static int dwc3_zynqmp_power_req(struct device *dev, bool on)
}

priv_data->pmu_state = D0_STATE;
/* disable D3 entry */
priv_data->enable_d3_suspend = false;
} else {
dev_dbg(priv_data->dev, "Trying to set power state to D3...\n");

Expand Down Expand Up @@ -505,6 +515,36 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
return ret;
}

/* xilinx feature support functions */
static void dwc3_xilinx_wakeup_capable(struct device *dev, bool wakeup)
{
struct device_node *node = of_node_get(dev->parent->of_node);

/* check for valid parent node */
while (node) {
if (of_device_is_compatible(node, "xlnx,zynqmp-dwc3") ||
of_device_is_compatible(node, "xlnx,versal-dwc3"))
break;

/* get the next parent node */
node = of_get_next_parent(node);
}

if (node) {
struct platform_device *pdev_parent;
struct dwc3_xlnx *priv_data;

pdev_parent = of_find_device_by_node(node);
priv_data = platform_get_drvdata(pdev_parent);

/* Set wakeup capable as true or false */
priv_data->wakeup_capable = wakeup;

/* Allow D3 state if wakeup capable only */
priv_data->enable_d3_suspend = wakeup;
}
}

static const struct of_device_id dwc3_xlnx_of_match[] = {
{
.compatible = "xlnx,zynqmp-dwc3",
Expand Down Expand Up @@ -559,13 +599,21 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)

of_node_put(dwc3_child_node);

/*
* TODO: This flag needs to be handled while implementing
* the remote wake-up feature.
*/
priv_data->enable_d3_suspend = false;

platform_set_drvdata(pdev, priv_data);

#ifdef CONFIG_PM
ret = dwc3_xlnx_register_regulator(dev, priv_data);
if (ret)
return ret;
#endif
/* Register the dwc3-xilinx wakeup function to dwc3 host */
dwc3_host_wakeup_register(dwc3_xilinx_wakeup_capable);

platform_set_drvdata(pdev, priv_data);

Expand Down Expand Up @@ -607,6 +655,8 @@ static int dwc3_xlnx_remove(struct platform_device *pdev)

of_platform_depopulate(dev);

/* Unregister the dwc3-xilinx wakeup function from dwc3 host */
dwc3_host_wakeup_register(NULL);
clk_bulk_disable_unprepare(priv_data->num_clocks, priv_data->clks);
priv_data->num_clocks = 0;

Expand Down Expand Up @@ -645,17 +695,18 @@ static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
{
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);

if (!priv_data->wakeup_capable) {
#ifdef CONFIG_PM
if (priv_data->dr_mode == USB_DR_MODE_PERIPHERAL)
/* Put the core into D3 */
dwc3_set_usb_core_power(dev, false);
if (priv_data->dr_mode == USB_DR_MODE_PERIPHERAL)
/* Put the core into D3 */
dwc3_set_usb_core_power(dev, false);
#endif

phy_exit(priv_data->usb3_phy);

/* Disable the clocks */
clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
phy_exit(priv_data->usb3_phy);

/* Disable the clocks */
clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
}
return 0;
}

Expand All @@ -664,6 +715,9 @@ static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
int ret;

if (priv_data->wakeup_capable)
return 0;

#ifdef CONFIG_PM
if (priv_data->dr_mode == USB_DR_MODE_PERIPHERAL)
/* Put the core into D0 */
Expand Down
15 changes: 15 additions & 0 deletions drivers/usb/dwc3/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@

#include "core.h"

static dwc3_wakeup_t dwc3_wakeup_fn;

/* dwc3 host wakeup registration */
void dwc3_host_wakeup_register(dwc3_wakeup_t func)
{
dwc3_wakeup_fn = func;
}

/* callback function */
void dwc3_host_wakeup_capable(struct device *dev, bool wakeup)
{
if (dwc3_wakeup_fn)
dwc3_wakeup_fn(dev, wakeup);
}

static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
int irq, char *name)
{
Expand Down
30 changes: 30 additions & 0 deletions drivers/usb/host/xhci-plat.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);

static host_wakeup_t host_wakeup_fn;

static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
Expand Down Expand Up @@ -87,6 +89,17 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_PLAT | priv->quirks;
}

static void host_wakeup_register(host_wakeup_t func)
{
host_wakeup_fn = func;
}

static void host_wakeup_capable(struct device *dev, bool wakeup)
{
if (host_wakeup_fn)
host_wakeup_fn(dev, wakeup);
}

/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
Expand Down Expand Up @@ -253,6 +266,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
return ret;

/* Set the controller as wakeup capable */
device_set_wakeup_capable(&pdev->dev, true);

pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
Expand Down Expand Up @@ -313,6 +329,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
}

device_set_wakeup_capable(&pdev->dev, true);
host_wakeup_register(dwc3_host_wakeup_capable);

xhci->main_hcd = hcd;

Expand Down Expand Up @@ -442,6 +459,7 @@ static int xhci_plat_remove(struct platform_device *dev)

usb_phy_shutdown(hcd->usb_phy);

host_wakeup_register(NULL);
usb_otg_set_host(&dev->dev, hcd, false);

usb_remove_hcd(hcd);
Expand Down Expand Up @@ -472,6 +490,15 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
ret = xhci_priv_suspend_quirk(hcd);
if (ret)
return ret;

/* Inform dwc3 driver about the device wakeup capability */
if (device_may_wakeup(&hcd->self.root_hub->dev)) {
host_wakeup_capable(dev, true);
enable_irq_wake(hcd->irq);
} else {
host_wakeup_capable(dev, false);
}

/*
* xhci_suspend() needs `do_wakeup` to know whether host is allowed
* to do wakeup during suspend.
Expand Down Expand Up @@ -532,6 +559,9 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);

if (device_may_wakeup(&hcd->self.root_hub->dev))
disable_irq_wake(hcd->irq);

return xhci_resume(xhci, 0);
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,8 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,

/* xHCI host controller glue */
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
typedef void (*host_wakeup_t)(struct device *dev, bool wakeup);
void dwc3_host_wakeup_capable(struct device *dev, bool wakeup);
int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
Expand Down

0 comments on commit 22f32ef

Please sign in to comment.