From patchwork Wed Oct 17 15:34:01 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felipe Balbi X-Patchwork-Id: 1606731 Return-Path: X-Original-To: patchwork-linux-omap@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 67F41E00B1 for ; Wed, 17 Oct 2012 15:39:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932327Ab2JQPjw (ORCPT ); Wed, 17 Oct 2012 11:39:52 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:44409 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932322Ab2JQPjs (ORCPT ); Wed, 17 Oct 2012 11:39:48 -0400 Received: from dlelxv30.itg.ti.com ([172.17.2.17]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id q9HFdgoT004856; Wed, 17 Oct 2012 10:39:42 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv30.itg.ti.com (8.13.8/8.13.8) with ESMTP id q9HFdgwh001393; Wed, 17 Oct 2012 10:39:42 -0500 Received: from dlelxv22.itg.ti.com (172.17.1.197) by dfle72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.1.323.3; Wed, 17 Oct 2012 10:39:41 -0500 Received: from localhost (h68-7.vpn.ti.com [172.24.68.7]) by dlelxv22.itg.ti.com (8.13.8/8.13.8) with ESMTP id q9HFdf5V021544; Wed, 17 Oct 2012 10:39:41 -0500 From: Felipe Balbi To: Tony Lindgren CC: Paul Walmsley , Kevin Hilman , Santosh Shilimkar , Linux OMAP Mailing List , Linux ARM Kernel Mailing List , Felipe Balbi Subject: [RFC/NOT FOR MERGING 3/5] arm: omap: introduce other PM methods Date: Wed, 17 Oct 2012 18:34:01 +0300 Message-ID: <1350488043-5053-4-git-send-email-balbi@ti.com> X-Mailer: git-send-email 1.8.0.rc0 In-Reply-To: <1350488043-5053-1-git-send-email-balbi@ti.com> References: <1350488043-5053-1-git-send-email-balbi@ti.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org current omap_device PM implementation defines omap-specific *_noirq methods but uses the generic versions for all other PM methods. As it turns out, if a device decides to implement non-runtime PM callbacks, we might fall into a situation where the hwmod is still idled which will generate an abort exception when we try to access device's address space while clocks are still gated. In order to solve that, we implement all other methods taking into account that devices might not implement those, in which case we return early. Signed-off-by: Felipe Balbi --- notice here that it would be far better to avoid the code duplication and have another function (e.g. _od_generic_suspend/resume) which would receive pm_message_t and omap_device as arguments so it can decide what to do. That can be done on a later version, though. arch/arm/plat-omap/omap_device.c | 145 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c index cd84eac..60ce750 100644 --- a/arch/arm/plat-omap/omap_device.c +++ b/arch/arm/plat-omap/omap_device.c @@ -843,16 +843,159 @@ static int _od_resume_noirq(struct device *dev) return pm_generic_resume_noirq(dev); } + +static int _od_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + if (!pm || !pm->suspend) + return 0; + + /* Don't attempt suspend on a driver that is not bound */ + if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) + return 0; + + ret = pm_generic_suspend(dev); + if (ret) + return ret; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_idle(pdev); + od->flags |= OMAP_DEVICE_SUSPENDED; + + return 0; +} + +static int _od_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->resume) + return 0; + + if (od->flags & OMAP_DEVICE_SUSPENDED) { + od->flags &= ~OMAP_DEVICE_SUSPENDED; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_enable(pdev); + } + + return pm_generic_resume(dev); +} + +static int _od_freeze(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + if (!pm || !pm->freeze) + return 0; + + /* Don't attempt late suspend on a driver that is not bound */ + if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) + return 0; + + ret = pm_generic_freeze(dev); + if (ret) + return ret; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_idle(pdev); + od->flags |= OMAP_DEVICE_SUSPENDED; + + return 0; +} + +static int _od_thaw(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->thaw) + return 0; + + if (od->flags & OMAP_DEVICE_SUSPENDED) { + od->flags &= ~OMAP_DEVICE_SUSPENDED; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_enable(pdev); + } + + return pm_generic_thaw(dev); +} + +static int _od_poweroff(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + if (!pm || !pm->poweroff) + return 0; + + /* Don't attempt late suspend on a driver that is not bound */ + if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) + return 0; + + ret = pm_generic_poweroff(dev); + if (ret) + return ret; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_idle(pdev); + od->flags |= OMAP_DEVICE_SUSPENDED; + + return 0; +} + +static int _od_restore(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->restore) + return 0; + + if (od->flags & OMAP_DEVICE_SUSPENDED) { + od->flags &= ~OMAP_DEVICE_SUSPENDED; + + if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)) + omap_device_enable(pdev); + } + + return pm_generic_restore(dev); +} #else #define _od_suspend_noirq NULL #define _od_resume_noirq NULL +#define _od_suspend NULL +#define _od_resume NULL +#define _od_freeze NULL +#define _od_thaw NULL +#define _od_poweroff NULL +#define _od_restore NULL #endif struct dev_pm_domain omap_device_pm_domain = { .ops = { SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, _od_runtime_idle) - USE_PLATFORM_PM_SLEEP_OPS + .suspend = _od_suspend, + .resume = _od_resume, + .freeze = _od_freeze, + .thaw = _od_thaw, + .poweroff = _od_poweroff, + .restore = _od_restore, .suspend_noirq = _od_suspend_noirq, .resume_noirq = _od_resume_noirq, }