From patchwork Wed Mar 18 15:02:11 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Rafael J. Wysocki" X-Patchwork-Id: 6040251 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 88B949F399 for ; Wed, 18 Mar 2015 14:41:01 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 830AE204FB for ; Wed, 18 Mar 2015 14:41:00 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 864B0203B1 for ; Wed, 18 Mar 2015 14:40:59 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YYF7K-00078W-0f; Wed, 18 Mar 2015 14:38:42 +0000 Received: from v094114.home.net.pl ([79.96.170.134]) by bombadil.infradead.org with smtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YYF7H-0006sb-3F for linux-arm-kernel@lists.infradead.org; Wed, 18 Mar 2015 14:38:40 +0000 Received: from aeqx230.neoplus.adsl.tpnet.pl (79.191.179.230) (HELO vostro.rjw.lan) by serwer1319399.home.pl (79.96.170.134) with SMTP (IdeaSmtpServer v0.80) id 9a0494c5ccec212c; Wed, 18 Mar 2015 15:38:15 +0100 From: "Rafael J. Wysocki" To: Ulf Hansson , "linux-pm@vger.kernel.org" Subject: [PATCH] driver core / PM: Add callbacks for PM domain initialization/cleanup Date: Wed, 18 Mar 2015 16:02:11 +0100 Message-ID: <2317791.ICLpdqLgyu@vostro.rjw.lan> User-Agent: KMail/4.11.5 (Linux/3.19.0+; KDE/4.11.5; x86_64; ; ) In-Reply-To: References: <1426261429-31883-1-git-send-email-ulf.hansson@linaro.org> <2524443.vpfcNqfmJz@vostro.rjw.lan> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150318_073839_346718_39FB8A85 X-CRM114-Status: GOOD ( 20.32 ) X-Spam-Score: -0.0 (/) Cc: Len Brown , Kevin Hilman , Russell King , Geert Uytterhoeven , Wolfram Sang , Greg Kroah-Hartman , Dmitry Torokhov , Mark Brown , Pavel Machek , "linux-arm-kernel@lists.infradead.org" X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Rafael J. Wysocki If PM domains are in use, it may be necessary to prepare the code handling a PM domain for driver probing. For example, in some cases device drivers rely on the ability to power on the devices with the help of the IO runtime PM framework and the PM domain code needs to be ready for that. Also, if that code has not been fully initialized yet, the driver probing should be deferred. Moreover, after the probing is complete, it may be necessary to put the PM domain in question into the state reflecting the current needs of the devices in it, for example, to prevent power from being drawn in vain. For these reasons, introduce new PM domain callbacks, ->activate and ->sync, called, respectively, before probing for a device driver and after the probing has been completed. That is not sufficient, however, because the device's PM domain pointer has to be populated for the ->activate callback to be executed, so setting it in bus type ->probe callback routines would be too late. Also, there are bus types where PM domains are not used at all and the core should not attempt to set the pm_domain pointer for the devices on those buses. To overcome that difficulty, introduce two new bus type callbacks, ->init and ->release, called by bus_add_device() and bus_remove_device(), respectively. That will allow ->init to be used to populate the pm_domain pointer for the bus types that want to do that and ->release will be useful for any cleanup that may be necessary after removing a device that was part of a PM domain. Signed-off-by: Rafael J. Wysocki --- It occured to me that we might want to ->sync regardless of whether or not the probing had been succenssful, so I changed the code in really_probe() along these lines. Please let me know if that's not OK. --- drivers/base/bus.c | 12 +++++++++++- drivers/base/dd.c | 20 ++++++++++++++------ include/linux/device.h | 5 +++++ include/linux/pm.h | 6 ++++++ 4 files changed, 36 insertions(+), 7 deletions(-) Index: linux-pm/drivers/base/bus.c =================================================================== --- linux-pm.orig/drivers/base/bus.c +++ linux-pm/drivers/base/bus.c @@ -509,10 +509,15 @@ int bus_add_device(struct device *dev) int error = 0; if (bus) { + if (bus->init) { + error = bus->init(dev); + if (error) + goto out_put; + } pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev)); error = device_add_attrs(bus, dev); if (error) - goto out_put; + goto out_release; error = device_add_groups(dev, bus->dev_groups); if (error) goto out_groups; @@ -534,6 +539,9 @@ out_groups: device_remove_groups(dev, bus->dev_groups); out_id: device_remove_attrs(bus, dev); +out_release: + if (bus->release) + bus->release(dev); out_put: bus_put(dev->bus); return error; @@ -597,6 +605,8 @@ void bus_remove_device(struct device *de device_remove_groups(dev, dev->bus->dev_groups); if (klist_node_attached(&dev->p->knode_bus)) klist_del(&dev->p->knode_bus); + if (bus->release) + bus->release(dev); pr_debug("bus: '%s': remove device %s\n", dev->bus->name, dev_name(dev)); Index: linux-pm/include/linux/device.h =================================================================== --- linux-pm.orig/include/linux/device.h +++ linux-pm/include/linux/device.h @@ -69,6 +69,8 @@ extern void bus_remove_file(struct bus_t * @bus_groups: Default attributes of the bus. * @dev_groups: Default attributes of the devices on the bus. * @drv_groups: Default attributes of the device drivers on the bus. + * @init: Called when registering a device on this bus. + * @release: Called when unregistering a device on this bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a nonzero value if the * given device can be handled by the given driver. @@ -111,6 +113,9 @@ struct bus_type { const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; + int (*init)(struct device *dev); + void (*release)(struct device *dev); + int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); Index: linux-pm/include/linux/pm.h =================================================================== --- linux-pm.orig/include/linux/pm.h +++ linux-pm/include/linux/pm.h @@ -603,10 +603,16 @@ extern void dev_pm_put_subsys_data(struc * Power domains provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions along with * subsystem-level and driver-level callbacks. + * + * @detach: Called when removing a device from the domain. + * @activate: Called before executing probe routines for bus types and drivers. + * @sync: Called after successful execiton of the probe routines. */ struct dev_pm_domain { struct dev_pm_ops ops; void (*detach)(struct device *dev, bool power_off); + int (*activate)(struct device *dev); + void (*sync)(struct device *dev); }; /* Index: linux-pm/drivers/base/dd.c =================================================================== --- linux-pm.orig/drivers/base/dd.c +++ linux-pm/drivers/base/dd.c @@ -279,6 +279,7 @@ static int really_probe(struct device *d { int ret = 0; int local_trigger_count = atomic_read(&deferred_trigger_count); + struct dev_pm_domain *pm_domain = dev->pm_domain; atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", @@ -298,16 +299,23 @@ static int really_probe(struct device *d goto probe_failed; } - if (dev->bus->probe) { - ret = dev->bus->probe(dev); - if (ret) - goto probe_failed; - } else if (drv->probe) { - ret = drv->probe(dev); + if (pm_domain && pm_domain->activate) { + ret = pm_domain->activate(dev); if (ret) goto probe_failed; } + if (dev->bus->probe) + ret = dev->bus->probe(dev); + else if (drv->probe) + ret = drv->probe(dev); + + if (pm_domain && pm_domain->sync) + pm_domain->sync(dev); + + if (ret) + goto probe_failed; + driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n",