Skip to content

Commit

Permalink
Merge branch 'acpi-pm'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Rafael J. Wysocki committed Sep 1, 2015
2 parents 73990fc + e91a398 commit ef5f5de
Show file tree
Hide file tree
Showing 25 changed files with 1,981 additions and 24 deletions.
54 changes: 38 additions & 16 deletions drivers/acpi/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,43 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device)
Device Matching
-------------------------------------------------------------------------- */

static struct acpi_device *acpi_primary_dev_companion(struct acpi_device *adev,
const struct device *dev)
{
struct mutex *physical_node_lock = &adev->physical_node_lock;

mutex_lock(physical_node_lock);
if (list_empty(&adev->physical_node_list)) {
adev = NULL;
} else {
const struct acpi_device_physical_node *node;

node = list_first_entry(&adev->physical_node_list,
struct acpi_device_physical_node, node);
if (node->dev != dev)
adev = NULL;
}
mutex_unlock(physical_node_lock);
return adev;
}

/**
* acpi_device_is_first_physical_node - Is given dev first physical node
* @adev: ACPI companion device
* @dev: Physical device to check
*
* Function checks if given @dev is the first physical devices attached to
* the ACPI companion device. This distinction is needed in some cases
* where the same companion device is shared between many physical devices.
*
* Note that the caller have to provide valid @adev pointer.
*/
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev)
{
return !!acpi_primary_dev_companion(adev, dev);
}

/*
* acpi_companion_match() - Can we match via ACPI companion device
* @dev: Device in question
Expand All @@ -506,7 +543,6 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device)
struct acpi_device *acpi_companion_match(const struct device *dev)
{
struct acpi_device *adev;
struct mutex *physical_node_lock;

adev = ACPI_COMPANION(dev);
if (!adev)
Expand All @@ -515,21 +551,7 @@ struct acpi_device *acpi_companion_match(const struct device *dev)
if (list_empty(&adev->pnp.ids))
return NULL;

physical_node_lock = &adev->physical_node_lock;
mutex_lock(physical_node_lock);
if (list_empty(&adev->physical_node_list)) {
adev = NULL;
} else {
const struct acpi_device_physical_node *node;

node = list_first_entry(&adev->physical_node_list,
struct acpi_device_physical_node, node);
if (node->dev != dev)
adev = NULL;
}
mutex_unlock(physical_node_lock);

return adev;
return acpi_primary_dev_companion(adev, dev);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions drivers/acpi/device_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,14 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
if (dev->pm_domain)
return -EEXIST;

/*
* Only attach the power domain to the first device if the
* companion is shared by multiple. This is to prevent doing power
* management twice.
*/
if (!acpi_device_is_first_physical_node(adev, dev))
return -EBUSY;

acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func);
dev->pm_domain = &acpi_general_pm_domain;
if (power_on) {
Expand Down
2 changes: 2 additions & 0 deletions drivers/acpi/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev);

/* --------------------------------------------------------------------------
Device Matching and Notification
Expand Down
15 changes: 9 additions & 6 deletions drivers/acpi/power.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/*
* acpi_power.c - ACPI Bus Power Management ($Revision: 39 $)
* drivers/acpi/power.c - ACPI Power Resources management.
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2001 - 2015 Intel Corp.
* Author: Andy Grover <andrew.grover@intel.com>
* Author: Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
Expand All @@ -23,10 +25,11 @@
* ACPI power-managed devices may be controlled in two ways:
* 1. via "Device Specific (D-State) Control"
* 2. via "Power Resource Control".
* This module is used to manage devices relying on Power Resource Control.
* The code below deals with ACPI Power Resources control.
*
* An ACPI "power resource object" describes a software controllable power
* plane, clock plane, or other resource used by a power managed device.
* An ACPI "power resource object" represents a software controllable power
* plane, clock plane, or other resource depended on by a device.
*
* A device may rely on multiple power resources, and a power resource
* may be shared by multiple devices.
*/
Expand Down
1 change: 0 additions & 1 deletion drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
return 0;
}


bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
{
struct acpi_device_physical_node *pn;
Expand Down
43 changes: 43 additions & 0 deletions drivers/base/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions drivers/base/dd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/base/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
37 changes: 37 additions & 0 deletions drivers/base/power/qos.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
11 changes: 11 additions & 0 deletions drivers/base/power/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions drivers/dma/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ config INTEL_IOP_ADMA
help
Enable support for the Intel(R) IOP Series RAID engines.

config IDMA64
tristate "Intel integrated DMA 64-bit support"
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Enable DMA support for Intel Low Power Subsystem such as found on
Intel Skylake PCH.

source "drivers/dma/dw/Kconfig"

config AT_HDMAC
Expand Down
1 change: 1 addition & 0 deletions drivers/dma/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ obj-$(CONFIG_HSU_DMA) += hsu/
obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
obj-$(CONFIG_MV_XOR) += mv_xor.o
obj-$(CONFIG_IDMA64) += idma64.o
obj-$(CONFIG_DW_DMAC_CORE) += dw/
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
Expand Down
Loading

0 comments on commit ef5f5de

Please sign in to comment.