From 57e6fe7b888e17f814bd35b7700ded51aa9a6a83 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 5 Nov 2009 19:06:01 -0800 Subject: [PATCH 1/2] OMAP2+: PM: initial runtime PM core support Implement the new runtime PM framework as a thin layer on top of the omap_device API. OMAP specific runtime PM methods are registered with the as custom methods on the platform_bus. In order to determine if a device is an omap_device, its parent device is checked. All omap_devices have a new 'omap_device_parent_ device as their parent device, so checking for this parent is used to check for valid omap_devices. If a device is an omap_device, then the appropriate omap_device functions are called for it. If not, only the generic runtime PM functions are called. Device driver's ->runtime_idle() hook is called when the runtime PM usecount reaches zero for that device. Driver's ->runtime_suspend() hooks are called just before the device is disabled (via omap_device_idle()), and device driver ->runtime_resume() hooks are called just after device has been enabled (via omap_device_enable().) OMAP4 build support from Rajendra Nayak . OMAP2 build support from Charulatha V Cc: Rajendra Nayak Cc: Charulatha V Acked-by: Grant Likely Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/Makefile | 10 +++-- arch/arm/mach-omap2/pm_bus.c | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-omap2/pm_bus.c diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 88d3a1e920f5..b48ca6039d83 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -49,14 +49,18 @@ obj-$(CONFIG_ARCH_OMAP2) += sdrc2xxx.o # Power Management ifeq ($(CONFIG_PM),y) obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o -obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o -obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o cpuidle34xx.o -obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o +obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o +obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o cpuidle34xx.o pm_bus.o +obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o obj-$(CONFIG_PM_DEBUG) += pm-debug.o AFLAGS_sleep24xx.o :=-Wa,-march=armv6 AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a +ifeq ($(CONFIG_PM_VERBOSE),y) +CFLAGS_pm_bus.o += -DDEBUG +endif + endif # PRCM diff --git a/arch/arm/mach-omap2/pm_bus.c b/arch/arm/mach-omap2/pm_bus.c new file mode 100644 index 000000000000..784989f8f2f5 --- /dev/null +++ b/arch/arm/mach-omap2/pm_bus.c @@ -0,0 +1,85 @@ +/* + * Runtime PM support code for OMAP + * + * Author: Kevin Hilman, Deep Root Systems, LLC + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_PM_RUNTIME +int omap_pm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int r, ret = 0; + + dev_dbg(dev, "%s\n", __func__); + + ret = pm_generic_runtime_suspend(dev); + + if (!ret && dev->parent == &omap_device_parent) { + r = omap_device_idle(pdev); + WARN_ON(r); + } + + return ret; +}; + +int omap_pm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int r; + + dev_dbg(dev, "%s\n", __func__); + + if (dev->parent == &omap_device_parent) { + r = omap_device_enable(pdev); + WARN_ON(r); + } + + return pm_generic_runtime_resume(dev); +}; +#else +#define omap_pm_runtime_suspend NULL +#define omap_pm_runtime_resume NULL +#endif /* CONFIG_PM_RUNTIME */ + +static int __init omap_pm_runtime_init(void) +{ + const struct dev_pm_ops *pm; + struct dev_pm_ops *omap_pm; + + pm = platform_bus_get_pm_ops(); + if (!pm) { + pr_err("%s: unable to get dev_pm_ops from platform_bus\n", + __func__); + return -ENODEV; + } + + omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL); + if (!omap_pm) { + pr_err("%s: unable to alloc memory for new dev_pm_ops\n", + __func__); + return -ENOMEM; + } + + omap_pm->runtime_suspend = omap_pm_runtime_suspend; + omap_pm->runtime_resume = omap_pm_runtime_resume; + + platform_bus_set_pm_ops(omap_pm); + + return 0; +} +core_initcall(omap_pm_runtime_init); From e933ec7c4ebd7e7daaad67b22e5f4a9a0ab102ea Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 17 Jun 2010 15:48:43 -0700 Subject: [PATCH 2/2] OMAP1: PM: add simple runtime PM layer to manage clocks On OMAP1, we do not have omap_device + omap_hwmod to manage the device-specific idle, enable and shutdown. Instead, just enable/disable device clocks automatically at the runtime PM level. This allows drivers to not have any OMAP1 specific clock management and allows them to simply use the runtime PM API to manage clocks. OMAP1 compile fixes Manjunatha GK Cc: Manjunatha GK Signed-off-by: Kevin Hilman --- arch/arm/mach-omap1/Makefile | 2 +- arch/arm/mach-omap1/pm_bus.c | 98 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-omap1/pm_bus.c diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index facfaeb1ae5c..9a304d854e33 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_OMAP_MPU_TIMER) += time.o obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o # Power Management -obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_PM) += pm.o sleep.o pm_bus.o # DSP obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c new file mode 100644 index 000000000000..8b66392be745 --- /dev/null +++ b/arch/arm/mach-omap1/pm_bus.c @@ -0,0 +1,98 @@ +/* + * Runtime PM support code for OMAP1 + * + * Author: Kevin Hilman, Deep Root Systems, LLC + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_PM_RUNTIME +static int omap1_pm_runtime_suspend(struct device *dev) +{ + struct clk *iclk, *fclk; + int ret = 0; + + dev_dbg(dev, "%s\n", __func__); + + ret = pm_generic_runtime_suspend(dev); + + fclk = clk_get(dev, "fck"); + if (!IS_ERR(fclk)) { + clk_disable(fclk); + clk_put(fclk); + } + + iclk = clk_get(dev, "ick"); + if (!IS_ERR(iclk)) { + clk_disable(iclk); + clk_put(iclk); + } + + return 0; +}; + +static int omap1_pm_runtime_resume(struct device *dev) +{ + int ret = 0; + struct clk *iclk, *fclk; + + dev_dbg(dev, "%s\n", __func__); + + iclk = clk_get(dev, "ick"); + if (!IS_ERR(iclk)) { + clk_enable(iclk); + clk_put(iclk); + } + + fclk = clk_get(dev, "fck"); + if (!IS_ERR(fclk)) { + clk_enable(fclk); + clk_put(fclk); + } + + return pm_generic_runtime_resume(dev); +}; + +static int __init omap1_pm_runtime_init(void) +{ + const struct dev_pm_ops *pm; + struct dev_pm_ops *omap_pm; + + pm = platform_bus_get_pm_ops(); + if (!pm) { + pr_err("%s: unable to get dev_pm_ops from platform_bus\n", + __func__); + return -ENODEV; + } + + omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL); + if (!omap_pm) { + pr_err("%s: unable to alloc memory for new dev_pm_ops\n", + __func__); + return -ENOMEM; + } + + omap_pm->runtime_suspend = omap1_pm_runtime_suspend; + omap_pm->runtime_resume = omap1_pm_runtime_resume; + + platform_bus_set_pm_ops(omap_pm); + + return 0; +} +core_initcall(omap1_pm_runtime_init); +#endif /* CONFIG_PM_RUNTIME */