Re: [PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM
Thanks Dmitry. I will remove this. On 9/2/20 4:33 AM, Dmitry Osipenko wrote: > 31.08.2020 07:40, JC Kuo пишет: >> +err = devm_request_threaded_irq(&pdev->dev, tegra->padctl_irq, >> +NULL, >> +tegra_xusb_padctl_irq, >> +IRQF_ONESHOT | > >> +IRQF_TRIGGER_HIGH, > > Specifying trigger levels is meaningless for interrupts coming from a > device-tree because DT levels always take precedence. >
Re: [PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM
Hi Thierry, Thanks for review. I will amend accordingly and submit a new revision. JC On 8/31/20 8:50 PM, Thierry Reding wrote: > On Mon, Aug 31, 2020 at 12:40:43PM +0800, JC Kuo wrote: >> This commit implements the complete programming sequence for ELPG >> entry and exit. >> >> 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk() >> and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL >> sleepwalk and wake detection circuits to maintain USB lines level >> and respond to wake events (wake-on-connect, wake-on-disconnect, >> device-initiated-wake). >> >> 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk() >> and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and >> wake detection circuits. >> >> At runtime suspend, XUSB host controller can enter ELPG to reduce >> power consumption. When XUSB PADCTL wake detection circuit detects >> a wake event, an interrupt will be raised. xhci-tegra driver then >> will invoke pm_runtime_resume() for xhci-tegra. >> >> Runtime resume could also be triggered by protocol drivers, this is >> the host-initiated-wake event. At runtime resume, xhci-tegra driver >> brings XUSB host controller out of ELPG to handle the wake events. >> >> The same ELPG enter/exit procedure will be performed for system >> suspend/resume path so USB devices can remain connected across SC7. >> >> Signed-off-by: JC Kuo >> --- >> drivers/usb/host/xhci-tegra.c | 391 +++--- >> 1 file changed, 361 insertions(+), 30 deletions(-) >> >> diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c >> index ce6526c2caf6..9530cfc83f45 100644 >> --- a/drivers/usb/host/xhci-tegra.c >> +++ b/drivers/usb/host/xhci-tegra.c >> @@ -15,9 +15,11 @@ >> #include >> #include >> #include >> +#include >> #include >> #include >> #include >> +#include >> #include >> #include >> #include >> @@ -224,6 +226,7 @@ struct tegra_xusb { >> >> int xhci_irq; >> int mbox_irq; >> +int padctl_irq; >> >> void __iomem *ipfs_base; >> void __iomem *fpci_base; >> @@ -268,10 +271,13 @@ struct tegra_xusb { >> dma_addr_t phys; >> } fw; >> >> +bool suspended; >> struct tegra_xusb_context context; >> }; >> >> static struct hc_driver __read_mostly tegra_xhci_hc_driver; >> +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime); >> +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime); >> >> static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset) >> { >> @@ -657,6 +663,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void >> *data) >> >> mutex_lock(&tegra->lock); >> >> +if (pm_runtime_suspended(tegra->dev) || tegra->suspended) >> +goto out; >> + >> value = fpci_readl(tegra, tegra->soc->mbox.data_out); >> tegra_xusb_mbox_unpack(&msg, value); >> >> @@ -670,6 +679,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void >> *data) >> >> tegra_xusb_mbox_handle(tegra, &msg); >> >> +out: >> mutex_unlock(&tegra->lock); >> return IRQ_HANDLED; >> } >> @@ -812,12 +822,27 @@ static void tegra_xusb_phy_disable(struct tegra_xusb >> *tegra) >> >> static int tegra_xusb_runtime_suspend(struct device *dev) >> { >> -return 0; >> +struct tegra_xusb *tegra = dev_get_drvdata(dev); >> +int ret; >> + >> +synchronize_irq(tegra->mbox_irq); >> +mutex_lock(&tegra->lock); >> +ret = tegra_xusb_enter_elpg(tegra, true); >> +mutex_unlock(&tegra->lock); >> + >> +return ret; >> } >> >> static int tegra_xusb_runtime_resume(struct device *dev) >> { >> -return 0; >> +struct tegra_xusb *tegra = dev_get_drvdata(dev); >> +int err; >> + >> +mutex_lock(&tegra->lock); >> +err = tegra_xusb_exit_elpg(tegra, true); >> +mutex_unlock(&tegra->lock); >> + >> +return err; >> } >> >> #ifdef CONFIG_PM_SLEEP >> @@ -1121,6 +1146,22 @@ static int >> __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) >> return err; >> } >> >> +static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data) >> +{ >> +struct tegra_xusb *tegra = data; >> + >> +mutex_lock(&tegra->lock); >> +if (tegra->suspended) { >> +mutex_unlock(&tegra->lock); >> +return IRQ_HANDLED; >> +} >> +mutex_unlock(&tegra->lock); > > Blank lines before and after a block can help make this less cluttered. > >> + >> +pm_runtime_resume(tegra->dev); >> + >> +return IRQ_HANDLED; >> +} >> + >> static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) >> { >> int err; >> @@ -1244,6 +1285,51 @@ static void tegra_xhci_id_work(struct work_struct >> *work) >> } >> } >> >> +static bool is_usb2_otg_phy(struct tegra_xusb *tegra, int index) > > unsigned int index? > >> +{ >> +return (tegra->usbphy[index] != NULL); >> +} >> + >> +static bool is_usb3_otg_phy(st
Re: [PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM
31.08.2020 07:40, JC Kuo пишет: > + err = devm_request_threaded_irq(&pdev->dev, tegra->padctl_irq, > + NULL, > + tegra_xusb_padctl_irq, > + IRQF_ONESHOT | > + IRQF_TRIGGER_HIGH, Specifying trigger levels is meaningless for interrupts coming from a device-tree because DT levels always take precedence.
Re: [PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM
On Mon, Aug 31, 2020 at 12:40:43PM +0800, JC Kuo wrote: > This commit implements the complete programming sequence for ELPG > entry and exit. > > 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk() > and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL > sleepwalk and wake detection circuits to maintain USB lines level > and respond to wake events (wake-on-connect, wake-on-disconnect, > device-initiated-wake). > > 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk() > and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and > wake detection circuits. > > At runtime suspend, XUSB host controller can enter ELPG to reduce > power consumption. When XUSB PADCTL wake detection circuit detects > a wake event, an interrupt will be raised. xhci-tegra driver then > will invoke pm_runtime_resume() for xhci-tegra. > > Runtime resume could also be triggered by protocol drivers, this is > the host-initiated-wake event. At runtime resume, xhci-tegra driver > brings XUSB host controller out of ELPG to handle the wake events. > > The same ELPG enter/exit procedure will be performed for system > suspend/resume path so USB devices can remain connected across SC7. > > Signed-off-by: JC Kuo > --- > drivers/usb/host/xhci-tegra.c | 391 +++--- > 1 file changed, 361 insertions(+), 30 deletions(-) > > diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c > index ce6526c2caf6..9530cfc83f45 100644 > --- a/drivers/usb/host/xhci-tegra.c > +++ b/drivers/usb/host/xhci-tegra.c > @@ -15,9 +15,11 @@ > #include > #include > #include > +#include > #include > #include > #include > +#include > #include > #include > #include > @@ -224,6 +226,7 @@ struct tegra_xusb { > > int xhci_irq; > int mbox_irq; > + int padctl_irq; > > void __iomem *ipfs_base; > void __iomem *fpci_base; > @@ -268,10 +271,13 @@ struct tegra_xusb { > dma_addr_t phys; > } fw; > > + bool suspended; > struct tegra_xusb_context context; > }; > > static struct hc_driver __read_mostly tegra_xhci_hc_driver; > +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime); > +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime); > > static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset) > { > @@ -657,6 +663,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void > *data) > > mutex_lock(&tegra->lock); > > + if (pm_runtime_suspended(tegra->dev) || tegra->suspended) > + goto out; > + > value = fpci_readl(tegra, tegra->soc->mbox.data_out); > tegra_xusb_mbox_unpack(&msg, value); > > @@ -670,6 +679,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void > *data) > > tegra_xusb_mbox_handle(tegra, &msg); > > +out: > mutex_unlock(&tegra->lock); > return IRQ_HANDLED; > } > @@ -812,12 +822,27 @@ static void tegra_xusb_phy_disable(struct tegra_xusb > *tegra) > > static int tegra_xusb_runtime_suspend(struct device *dev) > { > - return 0; > + struct tegra_xusb *tegra = dev_get_drvdata(dev); > + int ret; > + > + synchronize_irq(tegra->mbox_irq); > + mutex_lock(&tegra->lock); > + ret = tegra_xusb_enter_elpg(tegra, true); > + mutex_unlock(&tegra->lock); > + > + return ret; > } > > static int tegra_xusb_runtime_resume(struct device *dev) > { > - return 0; > + struct tegra_xusb *tegra = dev_get_drvdata(dev); > + int err; > + > + mutex_lock(&tegra->lock); > + err = tegra_xusb_exit_elpg(tegra, true); > + mutex_unlock(&tegra->lock); > + > + return err; > } > > #ifdef CONFIG_PM_SLEEP > @@ -1121,6 +1146,22 @@ static int > __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) > return err; > } > > +static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data) > +{ > + struct tegra_xusb *tegra = data; > + > + mutex_lock(&tegra->lock); > + if (tegra->suspended) { > + mutex_unlock(&tegra->lock); > + return IRQ_HANDLED; > + } > + mutex_unlock(&tegra->lock); Blank lines before and after a block can help make this less cluttered. > + > + pm_runtime_resume(tegra->dev); > + > + return IRQ_HANDLED; > +} > + > static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) > { > int err; > @@ -1244,6 +1285,51 @@ static void tegra_xhci_id_work(struct work_struct > *work) > } > } > > +static bool is_usb2_otg_phy(struct tegra_xusb *tegra, int index) unsigned int index? > +{ > + return (tegra->usbphy[index] != NULL); > +} > + > +static bool is_usb3_otg_phy(struct tegra_xusb *tegra, int index) Here too. > +{ > + struct tegra_xusb_padctl *padctl = tegra->padctl; > + int i, port; These can also be unsigned. > + > + for (i = 0; i < tegra->num_usb_phys; i++) { > + if (is_usb2_o
[PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM
This commit implements the complete programming sequence for ELPG entry and exit. 1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk() and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL sleepwalk and wake detection circuits to maintain USB lines level and respond to wake events (wake-on-connect, wake-on-disconnect, device-initiated-wake). 2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk() and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and wake detection circuits. At runtime suspend, XUSB host controller can enter ELPG to reduce power consumption. When XUSB PADCTL wake detection circuit detects a wake event, an interrupt will be raised. xhci-tegra driver then will invoke pm_runtime_resume() for xhci-tegra. Runtime resume could also be triggered by protocol drivers, this is the host-initiated-wake event. At runtime resume, xhci-tegra driver brings XUSB host controller out of ELPG to handle the wake events. The same ELPG enter/exit procedure will be performed for system suspend/resume path so USB devices can remain connected across SC7. Signed-off-by: JC Kuo --- drivers/usb/host/xhci-tegra.c | 391 +++--- 1 file changed, 361 insertions(+), 30 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index ce6526c2caf6..9530cfc83f45 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -224,6 +226,7 @@ struct tegra_xusb { int xhci_irq; int mbox_irq; + int padctl_irq; void __iomem *ipfs_base; void __iomem *fpci_base; @@ -268,10 +271,13 @@ struct tegra_xusb { dma_addr_t phys; } fw; + bool suspended; struct tegra_xusb_context context; }; static struct hc_driver __read_mostly tegra_xhci_hc_driver; +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime); +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime); static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset) { @@ -657,6 +663,9 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) mutex_lock(&tegra->lock); + if (pm_runtime_suspended(tegra->dev) || tegra->suspended) + goto out; + value = fpci_readl(tegra, tegra->soc->mbox.data_out); tegra_xusb_mbox_unpack(&msg, value); @@ -670,6 +679,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) tegra_xusb_mbox_handle(tegra, &msg); +out: mutex_unlock(&tegra->lock); return IRQ_HANDLED; } @@ -812,12 +822,27 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra) static int tegra_xusb_runtime_suspend(struct device *dev) { - return 0; + struct tegra_xusb *tegra = dev_get_drvdata(dev); + int ret; + + synchronize_irq(tegra->mbox_irq); + mutex_lock(&tegra->lock); + ret = tegra_xusb_enter_elpg(tegra, true); + mutex_unlock(&tegra->lock); + + return ret; } static int tegra_xusb_runtime_resume(struct device *dev) { - return 0; + struct tegra_xusb *tegra = dev_get_drvdata(dev); + int err; + + mutex_lock(&tegra->lock); + err = tegra_xusb_exit_elpg(tegra, true); + mutex_unlock(&tegra->lock); + + return err; } #ifdef CONFIG_PM_SLEEP @@ -1121,6 +1146,22 @@ static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) return err; } +static irqreturn_t tegra_xusb_padctl_irq(int irq, void *data) +{ + struct tegra_xusb *tegra = data; + + mutex_lock(&tegra->lock); + if (tegra->suspended) { + mutex_unlock(&tegra->lock); + return IRQ_HANDLED; + } + mutex_unlock(&tegra->lock); + + pm_runtime_resume(tegra->dev); + + return IRQ_HANDLED; +} + static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) { int err; @@ -1244,6 +1285,51 @@ static void tegra_xhci_id_work(struct work_struct *work) } } +static bool is_usb2_otg_phy(struct tegra_xusb *tegra, int index) +{ + return (tegra->usbphy[index] != NULL); +} + +static bool is_usb3_otg_phy(struct tegra_xusb *tegra, int index) +{ + struct tegra_xusb_padctl *padctl = tegra->padctl; + int i, port; + + for (i = 0; i < tegra->num_usb_phys; i++) { + if (is_usb2_otg_phy(tegra, i)) { + port = tegra_xusb_padctl_get_usb3_companion(padctl,i); + if (index == port) + return true; + } + } + + return false; +} + +static bool is_host_mode_phy(struct tegra_xusb *tegra, int phy_type, int index) +{ + if (strcmp(tegra->soc->phy_types[phy_type].name, "hsic") == 0) + re