From 0d675e267068765ddf083346898f6e47bcb46bba Mon Sep 17 00:00:00 2001 From: haotienh Date: Wed, 12 Feb 2025 10:48:16 +0000 Subject: [PATCH] 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 Reviewed-by: EJ Hsu Reviewed-by: WK Tsai Reviewed-by: Wayne Chang Reviewed-by: Henry Lin Signed-off-by: Vishwaroop A Acked-by: Noah Wager Acked-by: Jacob Martin Signed-off-by: Noah Wager --- drivers/usb/host/xhci-tegra.c | 55 ++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 3e2ec9dcd2d7..04eee6c02bda 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -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, },