diff options
author | Fugang Duan <fugang.duan@nxp.com> | 2018-01-02 04:11:52 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2018-01-24 01:20:30 -0500 |
commit | 13d6eb20fc79a1e606307256dad4098375539a09 (patch) | |
tree | baa432a295e9a72e589696a6b250c72e1bcba369 | |
parent | fe34fbf93f87e9e0f78eeeb6f21b2fc310cb6080 (diff) |
i2c: imx-lpi2c: add runtime pm support
Add runtime pm support to dynamically manage the clock to avoid enable/disable
clock in frequently that can improve the i2c bus transfer performance.
And use pm_runtime_force_suspend/resume() instead of lpi2c_imx_suspend/resume().
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r-- | drivers/i2c/busses/i2c-imx-lpi2c.c | 68 |
1 files changed, 51 insertions, 17 deletions
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index e86801a63120..e6da2c7a9a3e 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/of_device.h> | 30 | #include <linux/of_device.h> |
31 | #include <linux/pinctrl/consumer.h> | 31 | #include <linux/pinctrl/consumer.h> |
32 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
33 | #include <linux/pm_runtime.h> | ||
33 | #include <linux/sched.h> | 34 | #include <linux/sched.h> |
34 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
35 | 36 | ||
@@ -90,6 +91,8 @@ | |||
90 | #define FAST_PLUS_MAX_BITRATE 3400000 | 91 | #define FAST_PLUS_MAX_BITRATE 3400000 |
91 | #define HIGHSPEED_MAX_BITRATE 5000000 | 92 | #define HIGHSPEED_MAX_BITRATE 5000000 |
92 | 93 | ||
94 | #define I2C_PM_TIMEOUT 10 /* ms */ | ||
95 | |||
93 | enum lpi2c_imx_mode { | 96 | enum lpi2c_imx_mode { |
94 | STANDARD, /* 100+Kbps */ | 97 | STANDARD, /* 100+Kbps */ |
95 | FAST, /* 400+Kbps */ | 98 | FAST, /* 400+Kbps */ |
@@ -274,8 +277,8 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) | |||
274 | unsigned int temp; | 277 | unsigned int temp; |
275 | int ret; | 278 | int ret; |
276 | 279 | ||
277 | ret = clk_enable(lpi2c_imx->clk); | 280 | ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent); |
278 | if (ret) | 281 | if (ret < 0) |
279 | return ret; | 282 | return ret; |
280 | 283 | ||
281 | temp = MCR_RST; | 284 | temp = MCR_RST; |
@@ -284,7 +287,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) | |||
284 | 287 | ||
285 | ret = lpi2c_imx_config(lpi2c_imx); | 288 | ret = lpi2c_imx_config(lpi2c_imx); |
286 | if (ret) | 289 | if (ret) |
287 | goto clk_disable; | 290 | goto rpm_put; |
288 | 291 | ||
289 | temp = readl(lpi2c_imx->base + LPI2C_MCR); | 292 | temp = readl(lpi2c_imx->base + LPI2C_MCR); |
290 | temp |= MCR_MEN; | 293 | temp |= MCR_MEN; |
@@ -292,8 +295,9 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) | |||
292 | 295 | ||
293 | return 0; | 296 | return 0; |
294 | 297 | ||
295 | clk_disable: | 298 | rpm_put: |
296 | clk_disable(lpi2c_imx->clk); | 299 | pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent); |
300 | pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent); | ||
297 | 301 | ||
298 | return ret; | 302 | return ret; |
299 | } | 303 | } |
@@ -306,7 +310,8 @@ static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx) | |||
306 | temp &= ~MCR_MEN; | 310 | temp &= ~MCR_MEN; |
307 | writel(temp, lpi2c_imx->base + LPI2C_MCR); | 311 | writel(temp, lpi2c_imx->base + LPI2C_MCR); |
308 | 312 | ||
309 | clk_disable(lpi2c_imx->clk); | 313 | pm_runtime_mark_last_busy(lpi2c_imx->adapter.dev.parent); |
314 | pm_runtime_put_autosuspend(lpi2c_imx->adapter.dev.parent); | ||
310 | 315 | ||
311 | return 0; | 316 | return 0; |
312 | } | 317 | } |
@@ -606,22 +611,31 @@ static int lpi2c_imx_probe(struct platform_device *pdev) | |||
606 | return ret; | 611 | return ret; |
607 | } | 612 | } |
608 | 613 | ||
614 | pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT); | ||
615 | pm_runtime_use_autosuspend(&pdev->dev); | ||
616 | pm_runtime_get_noresume(&pdev->dev); | ||
617 | pm_runtime_set_active(&pdev->dev); | ||
618 | pm_runtime_enable(&pdev->dev); | ||
619 | |||
609 | temp = readl(lpi2c_imx->base + LPI2C_PARAM); | 620 | temp = readl(lpi2c_imx->base + LPI2C_PARAM); |
610 | lpi2c_imx->txfifosize = 1 << (temp & 0x0f); | 621 | lpi2c_imx->txfifosize = 1 << (temp & 0x0f); |
611 | lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f); | 622 | lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f); |
612 | 623 | ||
613 | clk_disable(lpi2c_imx->clk); | ||
614 | |||
615 | ret = i2c_add_adapter(&lpi2c_imx->adapter); | 624 | ret = i2c_add_adapter(&lpi2c_imx->adapter); |
616 | if (ret) | 625 | if (ret) |
617 | goto clk_unprepare; | 626 | goto rpm_disable; |
627 | |||
628 | pm_runtime_mark_last_busy(&pdev->dev); | ||
629 | pm_runtime_put_autosuspend(&pdev->dev); | ||
618 | 630 | ||
619 | dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n"); | 631 | dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n"); |
620 | 632 | ||
621 | return 0; | 633 | return 0; |
622 | 634 | ||
623 | clk_unprepare: | 635 | rpm_disable: |
624 | clk_unprepare(lpi2c_imx->clk); | 636 | pm_runtime_put(&pdev->dev); |
637 | pm_runtime_disable(&pdev->dev); | ||
638 | pm_runtime_dont_use_autosuspend(&pdev->dev); | ||
625 | 639 | ||
626 | return ret; | 640 | return ret; |
627 | } | 641 | } |
@@ -632,28 +646,48 @@ static int lpi2c_imx_remove(struct platform_device *pdev) | |||
632 | 646 | ||
633 | i2c_del_adapter(&lpi2c_imx->adapter); | 647 | i2c_del_adapter(&lpi2c_imx->adapter); |
634 | 648 | ||
635 | clk_unprepare(lpi2c_imx->clk); | 649 | pm_runtime_disable(&pdev->dev); |
650 | pm_runtime_dont_use_autosuspend(&pdev->dev); | ||
636 | 651 | ||
637 | return 0; | 652 | return 0; |
638 | } | 653 | } |
639 | 654 | ||
640 | #ifdef CONFIG_PM_SLEEP | 655 | #ifdef CONFIG_PM_SLEEP |
641 | static int lpi2c_imx_suspend(struct device *dev) | 656 | static int lpi2c_runtime_suspend(struct device *dev) |
642 | { | 657 | { |
658 | struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); | ||
659 | |||
660 | clk_disable_unprepare(lpi2c_imx->clk); | ||
643 | pinctrl_pm_select_sleep_state(dev); | 661 | pinctrl_pm_select_sleep_state(dev); |
644 | 662 | ||
645 | return 0; | 663 | return 0; |
646 | } | 664 | } |
647 | 665 | ||
648 | static int lpi2c_imx_resume(struct device *dev) | 666 | static int lpi2c_runtime_resume(struct device *dev) |
649 | { | 667 | { |
668 | struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); | ||
669 | int ret; | ||
670 | |||
650 | pinctrl_pm_select_default_state(dev); | 671 | pinctrl_pm_select_default_state(dev); |
672 | ret = clk_prepare_enable(lpi2c_imx->clk); | ||
673 | if (ret) { | ||
674 | dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret); | ||
675 | return ret; | ||
676 | } | ||
651 | 677 | ||
652 | return 0; | 678 | return 0; |
653 | } | 679 | } |
654 | #endif | ||
655 | 680 | ||
656 | static SIMPLE_DEV_PM_OPS(imx_lpi2c_pm, lpi2c_imx_suspend, lpi2c_imx_resume); | 681 | static const struct dev_pm_ops lpi2c_pm_ops = { |
682 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | ||
683 | pm_runtime_force_resume) | ||
684 | SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend, | ||
685 | lpi2c_runtime_resume, NULL) | ||
686 | }; | ||
687 | #define IMX_LPI2C_PM (&lpi2c_pm_ops) | ||
688 | #else | ||
689 | #define IMX_LPI2C_PM NULL | ||
690 | #endif | ||
657 | 691 | ||
658 | static struct platform_driver lpi2c_imx_driver = { | 692 | static struct platform_driver lpi2c_imx_driver = { |
659 | .probe = lpi2c_imx_probe, | 693 | .probe = lpi2c_imx_probe, |
@@ -661,7 +695,7 @@ static struct platform_driver lpi2c_imx_driver = { | |||
661 | .driver = { | 695 | .driver = { |
662 | .name = DRIVER_NAME, | 696 | .name = DRIVER_NAME, |
663 | .of_match_table = lpi2c_imx_of_match, | 697 | .of_match_table = lpi2c_imx_of_match, |
664 | .pm = &imx_lpi2c_pm, | 698 | .pm = IMX_LPI2C_PM, |
665 | }, | 699 | }, |
666 | }; | 700 | }; |
667 | 701 | ||