Merge branch 'acpi-pm'
* acpi-pm: ACPI / bus: Move duplicate code to a separate new function mfd: Add support for Intel Sunrisepoint LPSS devices dmaengine: add a driver for Intel integrated DMA 64-bit mfd: make mfd_remove_devices() iterate in reverse order driver core: implement device_for_each_child_reverse() klist: implement klist_prev() Driver core: wakeup the parent device before trying probe ACPI / PM: Attach ACPI power domain only once PM / QoS: Make it possible to expose device latency tolerance to userspace ACPI / PM: Update the copyright notice and description of power.c
This commit is contained in:
@@ -1252,6 +1252,19 @@ void device_unregister(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_unregister);
|
||||
|
||||
static struct device *prev_device(struct klist_iter *i)
|
||||
{
|
||||
struct klist_node *n = klist_prev(i);
|
||||
struct device *dev = NULL;
|
||||
struct device_private *p;
|
||||
|
||||
if (n) {
|
||||
p = to_device_private_parent(n);
|
||||
dev = p->device;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct device *next_device(struct klist_iter *i)
|
||||
{
|
||||
struct klist_node *n = klist_next(i);
|
||||
@@ -1340,6 +1353,36 @@ int device_for_each_child(struct device *parent, void *data,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_for_each_child);
|
||||
|
||||
/**
|
||||
* device_for_each_child_reverse - device child iterator in reversed order.
|
||||
* @parent: parent struct device.
|
||||
* @fn: function to be called for each device.
|
||||
* @data: data for the callback.
|
||||
*
|
||||
* Iterate over @parent's child devices, and call @fn for each,
|
||||
* passing it @data.
|
||||
*
|
||||
* We check the return of @fn each time. If it returns anything
|
||||
* other than 0, we break out and return that value.
|
||||
*/
|
||||
int device_for_each_child_reverse(struct device *parent, void *data,
|
||||
int (*fn)(struct device *dev, void *data))
|
||||
{
|
||||
struct klist_iter i;
|
||||
struct device *child;
|
||||
int error = 0;
|
||||
|
||||
if (!parent->p)
|
||||
return 0;
|
||||
|
||||
klist_iter_init(&parent->p->klist_children, &i);
|
||||
while ((child = prev_device(&i)) && !error)
|
||||
error = fn(child, data);
|
||||
klist_iter_exit(&i);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_for_each_child_reverse);
|
||||
|
||||
/**
|
||||
* device_find_child - device iterator for locating a particular device.
|
||||
* @parent: parent struct device
|
||||
|
@@ -399,6 +399,8 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe);
|
||||
*
|
||||
* This function must be called with @dev lock held. When called for a
|
||||
* USB interface, @dev->parent lock must be held as well.
|
||||
*
|
||||
* If the device has a parent, runtime-resume the parent before driver probing.
|
||||
*/
|
||||
int driver_probe_device(struct device_driver *drv, struct device *dev)
|
||||
{
|
||||
@@ -410,10 +412,16 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
|
||||
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
|
||||
drv->bus->name, __func__, dev_name(dev), drv->name);
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
|
||||
pm_runtime_barrier(dev);
|
||||
ret = really_probe(dev, drv);
|
||||
pm_request_idle(dev);
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_put(dev->parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -507,11 +515,17 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
|
||||
|
||||
device_lock(dev);
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
|
||||
bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
|
||||
dev_dbg(dev, "async probe completed\n");
|
||||
|
||||
pm_request_idle(dev);
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_put(dev->parent);
|
||||
|
||||
device_unlock(dev);
|
||||
|
||||
put_device(dev);
|
||||
@@ -541,6 +555,9 @@ static int __device_attach(struct device *dev, bool allow_async)
|
||||
.want_async = false,
|
||||
};
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
|
||||
ret = bus_for_each_drv(dev->bus, NULL, &data,
|
||||
__device_attach_driver);
|
||||
if (!ret && allow_async && data.have_async) {
|
||||
@@ -557,6 +574,9 @@ static int __device_attach(struct device *dev, bool allow_async)
|
||||
} else {
|
||||
pm_request_idle(dev);
|
||||
}
|
||||
|
||||
if (dev->parent)
|
||||
pm_runtime_put(dev->parent);
|
||||
}
|
||||
out_unlock:
|
||||
device_unlock(dev);
|
||||
|
@@ -73,6 +73,8 @@ extern int pm_qos_sysfs_add_resume_latency(struct device *dev);
|
||||
extern void pm_qos_sysfs_remove_resume_latency(struct device *dev);
|
||||
extern int pm_qos_sysfs_add_flags(struct device *dev);
|
||||
extern void pm_qos_sysfs_remove_flags(struct device *dev);
|
||||
extern int pm_qos_sysfs_add_latency_tolerance(struct device *dev);
|
||||
extern void pm_qos_sysfs_remove_latency_tolerance(struct device *dev);
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
|
@@ -883,3 +883,40 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val)
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_qos_expose_latency_tolerance - Expose latency tolerance to userspace
|
||||
* @dev: Device whose latency tolerance to expose
|
||||
*/
|
||||
int dev_pm_qos_expose_latency_tolerance(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev->power.set_latency_tolerance)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev_pm_qos_sysfs_mtx);
|
||||
ret = pm_qos_sysfs_add_latency_tolerance(dev);
|
||||
mutex_unlock(&dev_pm_qos_sysfs_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_tolerance);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_hide_latency_tolerance - Hide latency tolerance from userspace
|
||||
* @dev: Device whose latency tolerance to hide
|
||||
*/
|
||||
void dev_pm_qos_hide_latency_tolerance(struct device *dev)
|
||||
{
|
||||
mutex_lock(&dev_pm_qos_sysfs_mtx);
|
||||
pm_qos_sysfs_remove_latency_tolerance(dev);
|
||||
mutex_unlock(&dev_pm_qos_sysfs_mtx);
|
||||
|
||||
/* Remove the request from user space now */
|
||||
pm_runtime_get_sync(dev);
|
||||
dev_pm_qos_update_user_latency_tolerance(dev,
|
||||
PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT);
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_tolerance);
|
||||
|
@@ -738,6 +738,17 @@ void pm_qos_sysfs_remove_flags(struct device *dev)
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group);
|
||||
}
|
||||
|
||||
int pm_qos_sysfs_add_latency_tolerance(struct device *dev)
|
||||
{
|
||||
return sysfs_merge_group(&dev->kobj,
|
||||
&pm_qos_latency_tolerance_attr_group);
|
||||
}
|
||||
|
||||
void pm_qos_sysfs_remove_latency_tolerance(struct device *dev)
|
||||
{
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group);
|
||||
}
|
||||
|
||||
void rpm_sysfs_remove(struct device *dev)
|
||||
{
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
|
||||
|
Reference in New Issue
Block a user