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(>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(>lock);
>>  
>> +if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
>> +goto out;
>> +
>>  value = fpci_readl(tegra, tegra->soc->mbox.data_out);
>>  tegra_xusb_mbox_unpack(, value);
>>  
>> @@ -670,6 +679,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
>> *data)
>>  
>>  tegra_xusb_mbox_handle(tegra, );
>>  
>> +out:
>>  mutex_unlock(>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(>lock);
>> +ret = tegra_xusb_enter_elpg(tegra, true);
>> +mutex_unlock(>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(>lock);
>> +err = tegra_xusb_exit_elpg(tegra, true);
>> +mutex_unlock(>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(>lock);
>> +if (tegra->suspended) {
>> +mutex_unlock(>lock);
>> +return IRQ_HANDLED;
>> +}
>> +mutex_unlock(>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.
> 
>> +{
>> +

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(>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(>lock);
>  
> + if (pm_runtime_suspended(tegra->dev) || tegra->suspended)
> + goto out;
> +
>   value = fpci_readl(tegra, tegra->soc->mbox.data_out);
>   tegra_xusb_mbox_unpack(, value);
>  
> @@ -670,6 +679,7 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void 
> *data)
>  
>   tegra_xusb_mbox_handle(tegra, );
>  
> +out:
>   mutex_unlock(>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(>lock);
> + ret = tegra_xusb_enter_elpg(tegra, true);
> + mutex_unlock(>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(>lock);
> + err = tegra_xusb_exit_elpg(tegra, true);
> + mutex_unlock(>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(>lock);
> + if (tegra->suspended) {
> + mutex_unlock(>lock);
> + return IRQ_HANDLED;
> + }
> + mutex_unlock(>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_otg_phy(tegra, i)) {
> + port =