Hi,

On 03/09/2015 04:04 PM, Mian Yousaf Kaukab wrote:
> From: Gregory Herrero <gregory.herr...@intel.com>
> 
> When suspending usb bus, phy driver may disable controller power.
> In this case, registers need to be saved on suspend and restored
> on resume.
> 
> Signed-off-by: Gregory Herrero <gregory.herr...@intel.com>
> ---
>  drivers/usb/dwc2/core.c | 376 
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/dwc2/core.h |  84 +++++++++++
>  2 files changed, 460 insertions(+)
> 
> diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
> index d5197d4..e858062 100644
> --- a/drivers/usb/dwc2/core.c
> +++ b/drivers/usb/dwc2/core.c
> @@ -56,6 +56,382 @@
>  #include "core.h"
>  #include "hcd.h"
>  
> +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
> +/**
> + * dwc2_backup_host_registers() - Backup controller host registers.
> + * When suspending usb bus, registers needs to be backuped
> + * if controller power is disabled once suspended.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct hregs_backup *hr;
> +     int i;
> +
> +     dev_dbg(hsotg->dev, "%s\n", __func__);
> +
> +     /* Backup Host regs */
> +     hr = hsotg->hr_backup;
> +     if (!hr) {
> +             hr = kmalloc(sizeof(*hr), GFP_KERNEL);

Where is kfree(hr)?

> +             if (!hr) {
> +                     dev_err(hsotg->dev, "%s: can't allocate host regs\n",
> +                                     __func__);
> +                     return -ENOMEM;
> +             }
> +
> +             hsotg->hr_backup = hr;
> +     }
> +     hr->hcfg = readl(hsotg->regs + HCFG);
> +     hr->haintmsk = readl(hsotg->regs + HAINTMSK);
> +     for (i = 0; i < hsotg->core_params->host_channels; ++i)
> +             hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i));
> +
> +     hr->hprt0 = readl(hsotg->regs + HPRT0);
> +     hr->hfir = readl(hsotg->regs + HFIR);
> +     return 0;
> +}
> +
> +/**
> + * dwc2_restore_host_registers() - Restore controller host registers.
> + * When resuming usb bus, device registers needs to be restored
> + * if controller power were disabled.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct hregs_backup *hr;
> +     int i;
> +
> +     dev_dbg(hsotg->dev, "%s\n", __func__);
> +
> +     /* Restore host regs */
> +     hr = hsotg->hr_backup;
> +     if (!hr) {
> +             dev_err(hsotg->dev, "%s: no host registers to restore\n",
> +                             __func__);
> +             return -EINVAL;
> +     }
> +
> +     writel(hr->hcfg, hsotg->regs + HCFG);
> +     writel(hr->haintmsk, hsotg->regs + HAINTMSK);
> +
> +     for (i = 0; i < hsotg->core_params->host_channels; ++i)
> +             writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
> +
> +     writel(hr->hprt0, hsotg->regs + HPRT0);
> +     writel(hr->hfir, hsotg->regs + HFIR);
> +
> +     return 0;
> +}
> +#else
> +static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
> +{ return 0; }
> +
> +static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
> +{ return 0; }
> +#endif
> +
> +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
> +     IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
> +/**
> + * dwc2_backup_device_registers() - Backup controller device registers.
> + * When suspending usb bus, registers needs to be backuped
> + * if controller power is disabled once suspended.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct dregs_backup *dr;
> +     int i;
> +
> +     dev_dbg(hsotg->dev, "%s\n", __func__);
> +
> +     /* Backup dev regs */
> +     dr = hsotg->dr_backup;
> +     if (!dr) {
> +             dr = kmalloc(sizeof(*dr), GFP_KERNEL);

Ditto, kfree(dr) needed.

> +             if (!dr) {
> +                     dev_err(hsotg->dev, "%s: can't allocate device regs\n",
> +                                     __func__);
> +                     return -ENOMEM;
> +             }
> +
> +             hsotg->dr_backup = dr;
> +     }
> +
> +     dr->dcfg = readl(hsotg->regs + DCFG);
> +     dr->dctl = readl(hsotg->regs + DCTL);
> +     dr->daintmsk = readl(hsotg->regs + DAINTMSK);
> +     dr->diepmsk = readl(hsotg->regs + DIEPMSK);
> +     dr->doepmsk = readl(hsotg->regs + DOEPMSK);
> +
> +     for (i = 0; i < hsotg->num_of_eps; i++) {
> +             /* Backup IN EPs */
> +             dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i));
> +
> +             /* Ensure DATA PID is correctly configured */
> +             if (dr->diepctl[i] & DXEPCTL_DPID)
> +                     dr->diepctl[i] |= DXEPCTL_SETD1PID;
> +             else
> +                     dr->diepctl[i] |= DXEPCTL_SETD0PID;
> +
> +             dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i));
> +             dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i));
> +
> +             /* Backup OUT EPs */
> +             dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i));
> +
> +             /* Ensure DATA PID is correctly configured */
> +             if (dr->doepctl[i] & DXEPCTL_DPID)
> +                     dr->doepctl[i] |= DXEPCTL_SETD1PID;
> +             else
> +                     dr->doepctl[i] |= DXEPCTL_SETD0PID;
> +
> +             dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i));
> +             dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i));
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + * dwc2_restore_device_registers() - Restore controller device registers.
> + * When resuming usb bus, device registers needs to be restored
> + * if controller power were disabled.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct dregs_backup *dr;
> +     u32 dctl;
> +     int i;
> +
> +     dev_dbg(hsotg->dev, "%s\n", __func__);
> +
> +     /* Restore dev regs */
> +     dr = hsotg->dr_backup;
> +     if (!dr) {
> +             dev_err(hsotg->dev, "%s: no device registers to restore\n",
> +                             __func__);
> +             return -EINVAL;
> +     }
> +
> +     writel(dr->dcfg, hsotg->regs + DCFG);
> +     writel(dr->dctl, hsotg->regs + DCTL);
> +     writel(dr->daintmsk, hsotg->regs + DAINTMSK);
> +     writel(dr->diepmsk, hsotg->regs + DIEPMSK);
> +     writel(dr->doepmsk, hsotg->regs + DOEPMSK);
> +
> +     for (i = 0; i < hsotg->num_of_eps; i++) {
> +             /* Restore IN EPs */
> +             writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
> +             writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
> +             writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
> +
> +             /* Restore OUT EPs */
> +             writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
> +             writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
> +             writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
> +     }
> +
> +     /* Set the Power-On Programming done bit */
> +     dctl = readl(hsotg->regs + DCTL);
> +     dctl |= DCTL_PWRONPRGDONE;
> +     writel(dctl, hsotg->regs + DCTL);
> +
> +     return 0;
> +}
> +#else
> +static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
> +{ return 0; }
> +
> +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
> +{ return 0; }
> +#endif
> +
> +/**
> + * dwc2_backup_global_registers() - Backup global controller registers.
> + * When suspending usb bus, registers needs to be backuped
> + * if controller power is disabled once suspended.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct gregs_backup *gr;
> +     int i;
> +
> +     /* Backup global regs */
> +     gr = hsotg->gr_backup;
> +     if (!gr) {
> +             gr = kmalloc(sizeof(*gr), GFP_KERNEL);


Ditto, kfree(gr) needed.

--
Best regards,
Robert

> +             if (!gr) {
> +                     dev_err(hsotg->dev, "%s: can't allocate global regs\n",
> +                                     __func__);
> +                     return -ENOMEM;
> +             }
> +
> +             hsotg->gr_backup = gr;
> +     }
> +
> +     gr->gotgctl = readl(hsotg->regs + GOTGCTL);
> +     gr->gintmsk = readl(hsotg->regs + GINTMSK);
> +     gr->gahbcfg = readl(hsotg->regs + GAHBCFG);
> +     gr->gusbcfg = readl(hsotg->regs + GUSBCFG);
> +     gr->grxfsiz = readl(hsotg->regs + GRXFSIZ);
> +     gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
> +     gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ);
> +     gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG);
> +     for (i = 0; i < MAX_EPS_CHANNELS; i++)
> +             gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i));
> +
> +     return 0;
> +}
> +
> +/**
> + * dwc2_restore_global_registers() - Restore controller global registers.
> + * When resuming usb bus, device registers needs to be restored
> + * if controller power were disabled.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
> +{
> +     struct gregs_backup *gr;
> +     int i;
> +
> +     dev_dbg(hsotg->dev, "%s\n", __func__);
> +
> +     /* Restore global regs */
> +     gr = hsotg->gr_backup;
> +     if (!gr) {
> +             dev_err(hsotg->dev, "%s: no global registers to restore\n",
> +                             __func__);
> +             return -EINVAL;
> +     }
> +
> +     writel(0xffffffff, hsotg->regs + GINTSTS);
> +     writel(gr->gotgctl, hsotg->regs + GOTGCTL);
> +     writel(gr->gintmsk, hsotg->regs + GINTMSK);
> +     writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
> +     writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
> +     writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
> +     writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
> +     writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
> +     writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
> +     for (i = 0; i < MAX_EPS_CHANNELS; i++)
> +             writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
> +
> +     return 0;
> +}
> +
> +/**
> + * dwc2_exit_hibernation() - Exit controller from Partial Power Down.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + * @restore: Controller registers need to be restored
> + */
> +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
> +{
> +     u32 pcgcctl;
> +     int ret = 0;
> +
> +     pcgcctl = readl(hsotg->regs + PCGCTL);
> +     pcgcctl &= ~PCGCTL_STOPPCLK;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +
> +     pcgcctl = readl(hsotg->regs + PCGCTL);
> +     pcgcctl &= ~PCGCTL_PWRCLMP;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +
> +     pcgcctl = readl(hsotg->regs + PCGCTL);
> +     pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +
> +     udelay(100);
> +     if (restore) {
> +             ret = dwc2_restore_global_registers(hsotg);
> +             if (ret) {
> +                     dev_err(hsotg->dev, "%s: failed to restore registers\n",
> +                                     __func__);
> +                     return ret;
> +             }
> +             if (dwc2_is_host_mode(hsotg)) {
> +                     ret = dwc2_restore_host_registers(hsotg);
> +                     if (ret) {
> +                             dev_err(hsotg->dev, "%s: failed to restore host 
> registers\n",
> +                                             __func__);
> +                             return ret;
> +                     }
> +             } else {
> +                     ret = dwc2_restore_device_registers(hsotg);
> +                     if (ret) {
> +                             dev_err(hsotg->dev, "%s: failed to restore 
> device registers\n",
> +                                             __func__);
> +                             return ret;
> +                     }
> +             }
> +     }
> +
> +     return ret;
> +}
> +
> +/**
> + * dwc2_enter_hibernation() - Put controller in Partial Power Down.
> + *
> + * @hsotg: Programming view of the DWC_otg controller
> + */
> +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
> +{
> +     u32 pcgcctl;
> +     int ret = 0;
> +
> +     /* Backup all registers */
> +     ret = dwc2_backup_global_registers(hsotg);
> +     if (ret) {
> +             dev_err(hsotg->dev, "%s: failed to backup global registers\n",
> +                             __func__);
> +             return ret;
> +     }
> +
> +     if (dwc2_is_host_mode(hsotg)) {
> +             ret = dwc2_backup_host_registers(hsotg);
> +             if (ret) {
> +                     dev_err(hsotg->dev, "%s: failed to backup host 
> registers\n",
> +                                     __func__);
> +                     return ret;
> +             }
> +     } else {
> +             ret = dwc2_backup_device_registers(hsotg);
> +             if (ret) {
> +                     dev_err(hsotg->dev, "%s: failed to backup device 
> registers\n",
> +                                     __func__);
> +                     return ret;
> +             }
> +     }
> +
> +     /* Put the controller in low power state */
> +     pcgcctl = readl(hsotg->regs + PCGCTL);
> +
> +     pcgcctl |= PCGCTL_PWRCLMP;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +     ndelay(20);
> +
> +     pcgcctl |= PCGCTL_RSTPDWNMODULE;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +     ndelay(20);
> +
> +     pcgcctl |= PCGCTL_STOPPCLK;
> +     writel(pcgcctl, hsotg->regs + PCGCTL);
> +
> +     return ret;
> +}
> +
>  /**
>   * dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
>   * used in both device and host modes
> diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
> index e60655f..d62b904 100644
> --- a/drivers/usb/dwc2/core.h
> +++ b/drivers/usb/dwc2/core.h
> @@ -452,6 +452,82 @@ struct dwc2_hw_params {
>  #define DWC2_CTRL_BUFF_SIZE 8
>  
>  /**
> + * struct gregs_backup - Holds global registers state before entering partial
> + * power down
> + * @gotgctl:         Backup of GOTGCTL register
> + * @gintmsk:         Backup of GINTMSK register
> + * @gahbcfg:         Backup of GAHBCFG register
> + * @gusbcfg:         Backup of GUSBCFG register
> + * @grxfsiz:         Backup of GRXFSIZ register
> + * @gnptxfsiz:               Backup of GNPTXFSIZ register
> + * @gi2cctl:         Backup of GI2CCTL register
> + * @hptxfsiz:                Backup of HPTXFSIZ register
> + * @gdfifocfg:               Backup of GDFIFOCFG register
> + * @dtxfsiz:         Backup of DTXFSIZ registers for each endpoint
> + * @gpwrdn:          Backup of GPWRDN register
> + */
> +struct gregs_backup {
> +     u32 gotgctl;
> +     u32 gintmsk;
> +     u32 gahbcfg;
> +     u32 gusbcfg;
> +     u32 grxfsiz;
> +     u32 gnptxfsiz;
> +     u32 gi2cctl;
> +     u32 hptxfsiz;
> +     u32 pcgcctl;
> +     u32 gdfifocfg;
> +     u32 dtxfsiz[MAX_EPS_CHANNELS];
> +     u32 gpwrdn;
> +};
> +
> +/**
> + * struct  dregs_backup - Holds device registers state before entering 
> partial
> + * power down
> + * @dcfg:            Backup of DCFG register
> + * @dctl:            Backup of DCTL register
> + * @daintmsk:                Backup of DAINTMSK register
> + * @diepmsk:         Backup of DIEPMSK register
> + * @doepmsk:         Backup of DOEPMSK register
> + * @diepctl:         Backup of DIEPCTL register
> + * @dieptsiz:                Backup of DIEPTSIZ register
> + * @diepdma:         Backup of DIEPDMA register
> + * @doepctl:         Backup of DOEPCTL register
> + * @doeptsiz:                Backup of DOEPTSIZ register
> + * @doepdma:         Backup of DOEPDMA register
> + */
> +struct dregs_backup {
> +     u32 dcfg;
> +     u32 dctl;
> +     u32 daintmsk;
> +     u32 diepmsk;
> +     u32 doepmsk;
> +     u32 diepctl[MAX_EPS_CHANNELS];
> +     u32 dieptsiz[MAX_EPS_CHANNELS];
> +     u32 diepdma[MAX_EPS_CHANNELS];
> +     u32 doepctl[MAX_EPS_CHANNELS];
> +     u32 doeptsiz[MAX_EPS_CHANNELS];
> +     u32 doepdma[MAX_EPS_CHANNELS];
> +};
> +
> +/**
> + * struct  hregs_backup - Holds host registers state before entering partial
> + * power down
> + * @hcfg:            Backup of HCFG register
> + * @haintmsk:                Backup of HAINTMSK register
> + * @hcintmsk:                Backup of HCINTMSK register
> + * @hptr0:           Backup of HPTR0 register
> + * @hfir:            Backup of HFIR register
> + */
> +struct hregs_backup {
> +     u32 hcfg;
> +     u32 haintmsk;
> +     u32 hcintmsk[MAX_EPS_CHANNELS];
> +     u32 hprt0;
> +     u32 hfir;
> +};
> +
> +/**
>   * struct dwc2_hsotg - Holds the state of the driver, including the 
> non-periodic
>   * and periodic schedules
>   *
> @@ -481,6 +557,9 @@ struct dwc2_hw_params {
>   *                      interrupt
>   * @wkp_timer:          Timer object for handling Wakeup Detected interrupt
>   * @lx_state:           Lx state of connected device
> + * @gregs_backup: Backup of global registers during suspend
> + * @dregs_backup: Backup of device registers during suspend
> + * @hregs_backup: Backup of host registers during suspend
>   *
>   * These are for host mode:
>   *
> @@ -611,6 +690,9 @@ struct dwc2_hsotg {
>       struct work_struct wf_otg;
>       struct timer_list wkp_timer;
>       enum dwc2_lx_state lx_state;
> +     struct gregs_backup *gr_backup;
> +     struct dregs_backup *dr_backup;
> +     struct hregs_backup *hr_backup;
>  
>       struct dentry *debug_root;
>       struct debugfs_regset32 *regset;
> @@ -747,6 +829,8 @@ enum dwc2_halt_status {
>   * and the DWC_otg controller
>   */
>  extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
> +extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
> +extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
>  
>  /*
>   * Host core Functions.
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to