Commit 62694735 authored by Sujit Reddy Thumma's avatar Sujit Reddy Thumma Committed by James Bottomley

[SCSI] ufs: Add runtime PM support for UFS host controller driver

Add runtime PM helpers to suspend/resume UFS controller at runtime.
Enable runtime PM by default for pci and platform drivers as the
initialized hardware can suspend if it is not used after bootup.
Signed-off-by: default avatarSujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: default avatarSantosh Y <santoshsy@gmail.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 66ec6d59
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "ufshcd.h" #include "ufshcd.h"
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pm_runtime.h>
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
...@@ -44,7 +45,7 @@ ...@@ -44,7 +45,7 @@
* *
* Returns -ENOSYS * Returns -ENOSYS
*/ */
static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) static int ufshcd_pci_suspend(struct device *dev)
{ {
/* /*
* TODO: * TODO:
...@@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state)
* *
* Returns -ENOSYS * Returns -ENOSYS
*/ */
static int ufshcd_pci_resume(struct pci_dev *pdev) static int ufshcd_pci_resume(struct device *dev)
{ {
/* /*
* TODO: * TODO:
...@@ -71,8 +72,45 @@ static int ufshcd_pci_resume(struct pci_dev *pdev) ...@@ -71,8 +72,45 @@ static int ufshcd_pci_resume(struct pci_dev *pdev)
return -ENOSYS; return -ENOSYS;
} }
#else
#define ufshcd_pci_suspend NULL
#define ufshcd_pci_resume NULL
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
static int ufshcd_pci_runtime_suspend(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_suspend(hba);
}
static int ufshcd_pci_runtime_resume(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_resume(hba);
}
static int ufshcd_pci_runtime_idle(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_idle(hba);
}
#else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pci_runtime_suspend NULL
#define ufshcd_pci_runtime_resume NULL
#define ufshcd_pci_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */
/** /**
* ufshcd_pci_shutdown - main function to put the controller in reset state * ufshcd_pci_shutdown - main function to put the controller in reset state
* @pdev: pointer to PCI device handle * @pdev: pointer to PCI device handle
...@@ -91,6 +129,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) ...@@ -91,6 +129,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
{ {
struct ufs_hba *hba = pci_get_drvdata(pdev); struct ufs_hba *hba = pci_get_drvdata(pdev);
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
disable_irq(pdev->irq); disable_irq(pdev->irq);
ufshcd_remove(hba); ufshcd_remove(hba);
pci_release_regions(pdev); pci_release_regions(pdev);
...@@ -168,6 +209,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -168,6 +209,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
} }
pci_set_drvdata(pdev, hba); pci_set_drvdata(pdev, hba);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_allow(&pdev->dev);
return 0; return 0;
...@@ -182,6 +225,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -182,6 +225,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err; return err;
} }
static const struct dev_pm_ops ufshcd_pci_pm_ops = {
.suspend = ufshcd_pci_suspend,
.resume = ufshcd_pci_resume,
.runtime_suspend = ufshcd_pci_runtime_suspend,
.runtime_resume = ufshcd_pci_runtime_resume,
.runtime_idle = ufshcd_pci_runtime_idle,
};
static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
{ PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ } /* terminate list */ { } /* terminate list */
...@@ -195,10 +246,9 @@ static struct pci_driver ufshcd_pci_driver = { ...@@ -195,10 +246,9 @@ static struct pci_driver ufshcd_pci_driver = {
.probe = ufshcd_pci_probe, .probe = ufshcd_pci_probe,
.remove = ufshcd_pci_remove, .remove = ufshcd_pci_remove,
.shutdown = ufshcd_pci_shutdown, .shutdown = ufshcd_pci_shutdown,
#ifdef CONFIG_PM .driver = {
.suspend = ufshcd_pci_suspend, .pm = &ufshcd_pci_pm_ops
.resume = ufshcd_pci_resume, },
#endif
}; };
module_pci_driver(ufshcd_pci_driver); module_pci_driver(ufshcd_pci_driver);
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
*/ */
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "ufshcd.h" #include "ufshcd.h"
...@@ -87,6 +88,40 @@ static int ufshcd_pltfrm_resume(struct device *dev) ...@@ -87,6 +88,40 @@ static int ufshcd_pltfrm_resume(struct device *dev)
#define ufshcd_pltfrm_resume NULL #define ufshcd_pltfrm_resume NULL
#endif #endif
#ifdef CONFIG_PM_RUNTIME
static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_suspend(hba);
}
static int ufshcd_pltfrm_runtime_resume(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_resume(hba);
}
static int ufshcd_pltfrm_runtime_idle(struct device *dev)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
if (!hba)
return 0;
return ufshcd_runtime_idle(hba);
}
#else /* !CONFIG_PM_RUNTIME */
#define ufshcd_pltfrm_runtime_suspend NULL
#define ufshcd_pltfrm_runtime_resume NULL
#define ufshcd_pltfrm_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */
/** /**
* ufshcd_pltfrm_probe - probe routine of the driver * ufshcd_pltfrm_probe - probe routine of the driver
* @pdev: pointer to Platform device handle * @pdev: pointer to Platform device handle
...@@ -122,14 +157,22 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) ...@@ -122,14 +157,22 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
goto out; goto out;
} }
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
err = ufshcd_init(dev, &hba, mmio_base, irq); err = ufshcd_init(dev, &hba, mmio_base, irq);
if (err) { if (err) {
dev_err(dev, "Intialization failed\n"); dev_err(dev, "Intialization failed\n");
goto out; goto out_disable_rpm;
} }
platform_set_drvdata(pdev, hba); platform_set_drvdata(pdev, hba);
return 0;
out_disable_rpm:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
out: out:
return err; return err;
} }
...@@ -144,6 +187,8 @@ static int ufshcd_pltfrm_remove(struct platform_device *pdev) ...@@ -144,6 +187,8 @@ static int ufshcd_pltfrm_remove(struct platform_device *pdev)
{ {
struct ufs_hba *hba = platform_get_drvdata(pdev); struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
disable_irq(hba->irq); disable_irq(hba->irq);
ufshcd_remove(hba); ufshcd_remove(hba);
return 0; return 0;
...@@ -157,6 +202,9 @@ static const struct of_device_id ufs_of_match[] = { ...@@ -157,6 +202,9 @@ static const struct of_device_id ufs_of_match[] = {
static const struct dev_pm_ops ufshcd_dev_pm_ops = { static const struct dev_pm_ops ufshcd_dev_pm_ops = {
.suspend = ufshcd_pltfrm_suspend, .suspend = ufshcd_pltfrm_suspend,
.resume = ufshcd_pltfrm_resume, .resume = ufshcd_pltfrm_resume,
.runtime_suspend = ufshcd_pltfrm_runtime_suspend,
.runtime_resume = ufshcd_pltfrm_runtime_resume,
.runtime_idle = ufshcd_pltfrm_runtime_idle,
}; };
static struct platform_driver ufshcd_pltfrm_driver = { static struct platform_driver ufshcd_pltfrm_driver = {
......
...@@ -2169,6 +2169,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ...@@ -2169,6 +2169,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
u32 status = 0; u32 status = 0;
hba = container_of(work, struct ufs_hba, eeh_work); hba = container_of(work, struct ufs_hba, eeh_work);
pm_runtime_get_sync(hba->dev);
err = ufshcd_get_ee_status(hba, &status); err = ufshcd_get_ee_status(hba, &status);
if (err) { if (err) {
dev_err(hba->dev, "%s: failed to get exception status %d\n", dev_err(hba->dev, "%s: failed to get exception status %d\n",
...@@ -2184,6 +2185,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ...@@ -2184,6 +2185,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
__func__, err); __func__, err);
} }
out: out:
pm_runtime_put_sync(hba->dev);
return; return;
} }
...@@ -2196,9 +2198,11 @@ static void ufshcd_fatal_err_handler(struct work_struct *work) ...@@ -2196,9 +2198,11 @@ static void ufshcd_fatal_err_handler(struct work_struct *work)
struct ufs_hba *hba; struct ufs_hba *hba;
hba = container_of(work, struct ufs_hba, feh_workq); hba = container_of(work, struct ufs_hba, feh_workq);
pm_runtime_get_sync(hba->dev);
/* check if reset is already in progress */ /* check if reset is already in progress */
if (hba->ufshcd_state != UFSHCD_STATE_RESET) if (hba->ufshcd_state != UFSHCD_STATE_RESET)
ufshcd_do_reset(hba); ufshcd_do_reset(hba);
pm_runtime_put_sync(hba->dev);
} }
/** /**
...@@ -2500,6 +2504,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) ...@@ -2500,6 +2504,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
ufshcd_force_reset_auto_bkops(hba); ufshcd_force_reset_auto_bkops(hba);
scsi_scan_host(hba->host); scsi_scan_host(hba->host);
pm_runtime_put_sync(hba->dev);
out: out:
return; return;
} }
...@@ -2721,6 +2726,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, ...@@ -2721,6 +2726,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
*hba_handle = hba; *hba_handle = hba;
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync(dev);
async_schedule(ufshcd_async_scan, hba); async_schedule(ufshcd_async_scan, hba);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment