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: 6040241 Return-Path: X-Original-To: patchwork-linux-pm@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 098ED9F314 for ; Wed, 18 Mar 2015 14:38:21 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E0EE320504 for ; Wed, 18 Mar 2015 14:38:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B4FBA204FB for ; Wed, 18 Mar 2015 14:38:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932810AbbCROiR (ORCPT ); Wed, 18 Mar 2015 10:38:17 -0400 Received: from v094114.home.net.pl ([79.96.170.134]:55089 "HELO v094114.home.net.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S932677AbbCROiR (ORCPT ); Wed, 18 Mar 2015 10:38:17 -0400 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" Cc: Len Brown , Pavel Machek , Kevin Hilman , Geert Uytterhoeven , Dmitry Torokhov , Russell King , Greg Kroah-Hartman , Mark Brown , Wolfram Sang , "linux-arm-kernel@lists.infradead.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 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, 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(-) -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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",