NVIDIA: SAUCE: xhci: tegra: Add remote wakeup support
This change gets USB wakeup IRQs which are under the PMC module. Then set the IRQ type to let PMC driver knows when to trigger wakeup. It enables those IRQs before suspend and disables them after resume. http://nvbugs/4804339 Signed-off-by: haotienh <haotienh@nvidia.com> Reviewed-by: EJ Hsu <ejh@nvidia.com> Reviewed-by: WK Tsai <wtsai@nvidia.com> Reviewed-by: Wayne Chang <waynec@nvidia.com> Reviewed-by: Henry Lin <henryl@nvidia.com> Signed-off-by: Vishwaroop A <va@nvidia.com> Acked-by: Noah Wager <noah.wager@canonical.com> Acked-by: Jacob Martin <jacob.martin@canonical.com> Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* NVIDIA Tegra xHCI host controller driver
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*/
|
||||
|
||||
@@ -257,6 +257,7 @@ struct tegra_xusb_soc {
|
||||
unsigned int num_supplies;
|
||||
const struct tegra_xusb_phy_type *phy_types;
|
||||
unsigned int num_types;
|
||||
unsigned int num_wakes;
|
||||
const struct tegra_xusb_context_soc *context;
|
||||
|
||||
struct {
|
||||
@@ -318,6 +319,7 @@ struct tegra_xusb {
|
||||
int xhci_irq;
|
||||
int mbox_irq;
|
||||
int padctl_irq;
|
||||
int *wake_irqs;
|
||||
|
||||
void __iomem *ipfs_base;
|
||||
void __iomem *fpci_base;
|
||||
@@ -2161,6 +2163,41 @@ static int tegra_xhci_padctl_notify(struct notifier_block *nb,
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xusb *tegra)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (device_property_read_bool(tegra->dev, "disable-wake"))
|
||||
return;
|
||||
|
||||
tegra->wake_irqs = devm_kcalloc(tegra->dev,
|
||||
tegra->soc->num_wakes,
|
||||
sizeof(*tegra->wake_irqs), GFP_KERNEL);
|
||||
if (!tegra->wake_irqs)
|
||||
return;
|
||||
|
||||
for (i = 0; i < tegra->soc->num_wakes; i++) {
|
||||
char irq_name[] = "wakeX";
|
||||
struct irq_data *data;
|
||||
|
||||
snprintf(irq_name, sizeof(irq_name), "wake%d", i);
|
||||
tegra->wake_irqs[i] = platform_get_irq_byname(pdev, irq_name);
|
||||
if (tegra->wake_irqs[i] < 0)
|
||||
goto error;
|
||||
data = irq_get_irq_data(tegra->wake_irqs[i]);
|
||||
if (!data)
|
||||
goto error;
|
||||
irq_set_irq_type(tegra->wake_irqs[i], irqd_get_trigger_type(data));
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs[i] >= 0; i++)
|
||||
irq_dispose_mapping(tegra->wake_irqs[i]);
|
||||
devm_kfree(tegra->dev, tegra->wake_irqs);
|
||||
tegra->wake_irqs = NULL;
|
||||
}
|
||||
|
||||
static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb *tegra;
|
||||
@@ -2220,6 +2257,9 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(tegra->padctl))
|
||||
return PTR_ERR(tegra->padctl);
|
||||
|
||||
if (tegra->soc->num_wakes && !tegra->soc->is_xhci_vf)
|
||||
tegra_xusb_setup_wakeup(pdev, tegra);
|
||||
|
||||
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
|
||||
if (!np) {
|
||||
err = -ENODEV;
|
||||
@@ -2585,6 +2625,7 @@ static void tegra_xusb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
|
||||
unsigned int i;
|
||||
|
||||
if (tegra->soc->is_xhci_vf)
|
||||
tegra_xusb_padctl_event_unregister(tegra->padctl, &tegra->padctl_nb);
|
||||
@@ -2607,6 +2648,9 @@ static void tegra_xusb_remove(struct platform_device *pdev)
|
||||
if (tegra->padctl_irq)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
|
||||
irq_dispose_mapping(tegra->wake_irqs[i]);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
tegra_xusb_disable(tegra);
|
||||
@@ -3066,8 +3110,12 @@ out:
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
if (tegra->padctl_irq && device_may_wakeup(dev)) {
|
||||
unsigned int i;
|
||||
|
||||
if (enable_irq_wake(tegra->padctl_irq))
|
||||
dev_err(dev, "failed to enable padctl wakes\n");
|
||||
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
|
||||
enable_irq_wake(tegra->wake_irqs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3095,8 +3143,12 @@ static __maybe_unused int tegra_xusb_resume(struct device *dev)
|
||||
}
|
||||
|
||||
if (tegra->padctl_irq && device_may_wakeup(dev)) {
|
||||
unsigned int i;
|
||||
|
||||
if (disable_irq_wake(tegra->padctl_irq))
|
||||
dev_err(dev, "failed to disable padctl wakes\n");
|
||||
for (i = 0; i < tegra->soc->num_wakes && tegra->wake_irqs; i++)
|
||||
disable_irq_wake(tegra->wake_irqs[i]);
|
||||
}
|
||||
tegra->suspended = false;
|
||||
mutex_unlock(&tegra->lock);
|
||||
@@ -3426,6 +3478,7 @@ static const struct tegra_xusb_soc tegra264_soc = {
|
||||
.num_supplies = ARRAY_SIZE(tegra194_supply_names),
|
||||
.phy_types = tegra194_phy_types,
|
||||
.num_types = ARRAY_SIZE(tegra194_phy_types),
|
||||
.num_wakes = 8,
|
||||
.context = &tegra186_xusb_context,
|
||||
.ports = {
|
||||
.usb3 = { .offset = 0, .count = 4, },
|
||||
|
||||
Reference in New Issue
Block a user