Re: [PATCH v2 12/12] xhci: tegra: enable ELPG for runtime/system PM

2020-09-07 Thread JC Kuo
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

2020-09-07 Thread JC Kuo
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

2020-09-01 Thread Dmitry Osipenko
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

2020-08-31 Thread Thierry Reding
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

2020-08-30 Thread JC Kuo
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