MLK-16713 i2c: imx-lpi2c: add runtime pm support
authorFugang Duan <fugang.duan@nxp.com>
Fri, 27 Oct 2017 02:12:42 +0000 (10:12 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:39:10 +0000 (15:39 -0500)
- Add runtime pm support to dynamicly manage the ipg and per clocks.
- Put the suspend to suspend_noirq.
- Call .pm_runtime_force_suspend() to force runtime pm suspended
  in .suspend_noirq().

BuildInfo:
  - SCFW 88456c73, IMX-MKIMAGE 06bc2767, ATF a438801
  - U-Boot 2017.03-imx_v2017.03_4.9.51_imx8_beta1+g7953d47

Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: Gao Pan <pandy.gao@nxp.com>
Reviewed-by: Anson Huang <Anson.Huang@nxp.com>
drivers/i2c/busses/i2c-imx-lpi2c.c

index 1bf93d7..f7491ea 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 
@@ -92,6 +93,8 @@
 #define FAST_PLUS_MAX_BITRATE  3400000
 #define HIGHSPEED_MAX_BITRATE  5000000
 
+#define I2C_PM_TIMEOUT         10 /* ms */
+
 enum lpi2c_imx_mode {
        STANDARD,       /* 100+Kbps */
        FAST,           /* 400+Kbps */
@@ -275,15 +278,9 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
        unsigned int temp;
        int ret;
 
-       ret = clk_enable(lpi2c_imx->clk_ipg);
-       if (ret)
-               return ret;
-
-       ret = clk_enable(lpi2c_imx->clk_per);
-       if (ret) {
-               clk_disable(lpi2c_imx->clk_ipg);
+       ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent);
+       if (ret < 0)
                return ret;
-       }
 
        temp = MCR_RST;
        writel(temp, lpi2c_imx->base + LPI2C_MCR);
@@ -291,7 +288,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
 
        ret = lpi2c_imx_config(lpi2c_imx);
        if (ret)
-               goto clk_disable;
+               goto rpm_put;
 
        temp = readl(lpi2c_imx->base + LPI2C_MCR);
        temp |= MCR_MEN;
@@ -299,9 +296,9 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
 
        return 0;
 
-clk_disable:
-       clk_disable(lpi2c_imx->clk_per);
-       clk_disable(lpi2c_imx->clk_ipg);
+rpm_put:
+       pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent);
+       pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent);
 
        return ret;
 }
@@ -314,8 +311,8 @@ static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
        temp &= ~MCR_MEN;
        writel(temp, lpi2c_imx->base + LPI2C_MCR);
 
-       clk_disable(lpi2c_imx->clk_per);
-       clk_disable(lpi2c_imx->clk_ipg);
+       pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent);
+       pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent);
 
        return 0;
 }
@@ -606,7 +603,8 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
        if (ret)
                lpi2c_imx->bitrate = LPI2C_DEFAULT_RATE;
 
-       ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,
+       ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr,
+                              IRQF_NO_SUSPEND,
                               pdev->name, lpi2c_imx);
        if (ret) {
                dev_err(&pdev->dev, "can't claim irq %d\n", irq);
@@ -616,30 +614,21 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
        i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
        platform_set_drvdata(pdev, lpi2c_imx);
 
-       ret = clk_prepare(lpi2c_imx->clk_per);
-       if (ret) {
-               dev_err(&pdev->dev, "clk prepare failed %d\n", ret);
-               return ret;
-       }
-
-       ret = clk_prepare(lpi2c_imx->clk_ipg);
-       if (ret) {
-               clk_unprepare(lpi2c_imx->clk_per);
-               dev_err(&pdev->dev, "clk prepare failed %d\n", ret);
-               return ret;
-       }
+       pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
 
        ret = i2c_add_adapter(&lpi2c_imx->adapter);
        if (ret)
-               goto clk_unprepare;
+               goto rpm_disable;
 
        dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
 
        return 0;
 
-clk_unprepare:
-       clk_unprepare(lpi2c_imx->clk_ipg);
-       clk_unprepare(lpi2c_imx->clk_per);
+rpm_disable:
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
 
        return ret;
 }
@@ -650,27 +639,69 @@ static int lpi2c_imx_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&lpi2c_imx->adapter);
 
-       clk_unprepare(lpi2c_imx->clk_ipg);
-       clk_unprepare(lpi2c_imx->clk_per);
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
 
        return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int lpi2c_imx_suspend(struct device *dev)
+static int lpi2c_runtime_suspend(struct device *dev)
 {
-       pinctrl_pm_select_sleep_state(dev);
+       struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(lpi2c_imx->clk_ipg);
+       clk_disable_unprepare(lpi2c_imx->clk_per);
+       pinctrl_pm_select_idle_state(dev);
+
        return 0;
 }
 
-static int lpi2c_imx_resume(struct device *dev)
+static int lpi2c_runtime_resume(struct device *dev)
 {
+       struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+       int ret;
+
        pinctrl_pm_select_default_state(dev);
+       ret = clk_prepare_enable(lpi2c_imx->clk_per);
+       if (ret) {
+               dev_err(dev, "can't enable I2C per clock, ret=%d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(lpi2c_imx->clk_ipg);
+       if (ret) {
+               clk_disable_unprepare(lpi2c_imx->clk_per);
+               dev_err(dev, "can't enable I2C ipg clock, ret=%d\n", ret);
+       }
+
+       return ret;
+}
+
+static int lpi2c_suspend_noirq(struct device *dev)
+{
+       int ret;
+
+       ret = pm_runtime_force_suspend(dev);
+       if (ret)
+               return ret;
+
+       pinctrl_pm_select_sleep_state(dev);
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(imx_lpi2c_pm, lpi2c_imx_suspend, lpi2c_imx_resume);
-#define IMX_LPI2C_PM      (&imx_lpi2c_pm)
+static int lpi2c_resume_noirq(struct device *dev)
+{
+       return pm_runtime_force_resume(dev);
+}
+
+static const struct dev_pm_ops lpi2c_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpi2c_suspend_noirq,
+                                    lpi2c_resume_noirq)
+       SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
+                          lpi2c_runtime_resume, NULL)
+};
+#define IMX_LPI2C_PM      (&lpi2c_pm_ops)
 #else
 #define IMX_LPI2C_PM      NULL
 #endif