Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 165f5fd0 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'pm-qos', 'pm-domains' and 'pm-drivers'

* pm-qos:
  PM / QoS: Add type to dev_pm_qos_add_ancestor_request() arguments
  ACPI / LPSS: Support for device latency tolerance PM QoS
  ACPI / scan: Add bind/unbind callbacks to struct acpi_scan_handler
  PM / QoS: Introcuce latency tolerance device PM QoS type
  PM / QoS: Add no_constraints_value field to struct pm_qos_constraints
  PM / QoS: Rename device resume latency QoS items

* pm-domains:
  PM / domains: Turn latency warning into debug message

* pm-drivers:
  PM: Add pm_runtime_suspend|resume_force functions
  PM / runtime: Fetch runtime PM callbacks using a macro
Loading
Loading
Loading
Loading
+26 −1
Original line number Diff line number Diff line
@@ -187,7 +187,7 @@ Description:
		Not all drivers support this attribute.  If it isn't supported,
		attempts to read or write it will yield I/O errors.

What:		/sys/devices/.../power/pm_qos_latency_us
What:		/sys/devices/.../power/pm_qos_resume_latency_us
Date:		March 2012
Contact:	Rafael J. Wysocki <rjw@rjwysocki.net>
Description:
@@ -205,6 +205,31 @@ Description:
		This attribute has no effect on system-wide suspend/resume and
		hibernation.

What:		/sys/devices/.../power/pm_qos_latency_tolerance_us
Date:		January 2014
Contact:	Rafael J. Wysocki <rjw@rjwysocki.net>
Description:
		The /sys/devices/.../power/pm_qos_latency_tolerance_us attribute
		contains the PM QoS active state latency tolerance limit for the
		given device in microseconds.  That is the maximum memory access
		latency the device can suffer without any visible adverse
		effects on user space functionality.  If that value is the
		string "any", the latency does not matter to user space at all,
		but hardware should not be allowed to set the latency tolerance
		for the device automatically.

		Reading "auto" from this file means that the maximum memory
		access latency for the device may be determined automatically
		by the hardware as needed.  Writing "auto" to it allows the
		hardware to be switched to this mode if there are no other
		latency tolerance requirements from the kernel side.

		This attribute is only present if the feature controlled by it
		is supported by the hardware.

		This attribute has no effect on runtime suspend and resume of
		devices and on system-wide suspend/resume and hibernation.

What:		/sys/devices/.../power/pm_qos_no_power_off
Date:		September 2012
Contact:	Rafael J. Wysocki <rjw@rjwysocki.net>
+60 −22
Original line number Diff line number Diff line
@@ -88,17 +88,19 @@ node.

2. PM QoS per-device latency and flags framework

For each device, there are two lists of PM QoS requests. One is maintained
along with the aggregated target of latency value and the other is for PM QoS
flags. Values are updated in response to changes of the request list.
For each device, there are three lists of PM QoS requests. Two of them are
maintained along with the aggregated targets of resume latency and active
state latency tolerance (in microseconds) and the third one is for PM QoS flags.
Values are updated in response to changes of the request list.

Target latency value is simply the minimum of the request values held in the
parameter list elements.  The PM QoS flags aggregate value is a gather (bitwise
OR) of all list elements' values. Two device PM QoS flags are defined currently:
PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP.
The target values of resume latency and active state latency tolerance are
simply the minimum of the request values held in the parameter list elements.
The PM QoS flags aggregate value is a gather (bitwise OR) of all list elements'
values.  Two device PM QoS flags are defined currently: PM_QOS_FLAG_NO_POWER_OFF
and PM_QOS_FLAG_REMOTE_WAKEUP.

Note: the aggregated target value is implemented as an atomic variable so that
reading the aggregated value does not require any locking mechanism.
Note: The aggregated target values are implemented in such a way that reading
the aggregated value does not require any locking mechanism.


From kernel mode the use of this interface is the following:
@@ -132,19 +134,21 @@ The meaning of the return values is as follows:
	PM_QOS_FLAGS_UNDEFINED: The device's PM QoS structure has not been
			initialized or the list of requests is empty.

int dev_pm_qos_add_ancestor_request(dev, handle, value)
int dev_pm_qos_add_ancestor_request(dev, handle, type, value)
Add a PM QoS request for the first direct ancestor of the given device whose
power.ignore_children flag is unset.
power.ignore_children flag is unset (for DEV_PM_QOS_RESUME_LATENCY requests)
or whose power.set_latency_tolerance callback pointer is not NULL (for
DEV_PM_QOS_LATENCY_TOLERANCE requests).

int dev_pm_qos_expose_latency_limit(device, value)
Add a request to the device's PM QoS list of latency constraints and create
a sysfs attribute pm_qos_resume_latency_us under the device's power directory
allowing user space to manipulate that request.
Add a request to the device's PM QoS list of resume latency constraints and
create a sysfs attribute pm_qos_resume_latency_us under the device's power
directory allowing user space to manipulate that request.

void dev_pm_qos_hide_latency_limit(device)
Drop the request added by dev_pm_qos_expose_latency_limit() from the device's
PM QoS list of latency constraints and remove sysfs attribute pm_qos_resume_latency_us
from the device's power directory.
PM QoS list of resume latency constraints and remove sysfs attribute
pm_qos_resume_latency_us from the device's power directory.

int dev_pm_qos_expose_flags(device, value)
Add a request to the device's PM QoS list of flags and create sysfs attributes
@@ -163,7 +167,7 @@ a per-device notification tree and a global notification tree.
int dev_pm_qos_add_notifier(device, notifier):
Adds a notification callback function for the device.
The callback is called when the aggregated value of the device constraints list
is changed.
is changed (for resume latency device PM QoS only).

int dev_pm_qos_remove_notifier(device, notifier):
Removes the notification callback function for the device.
@@ -171,14 +175,48 @@ Removes the notification callback function for the device.
int dev_pm_qos_add_global_notifier(notifier):
Adds a notification callback function in the global notification tree of the
framework.
The callback is called when the aggregated value for any device is changed.
The callback is called when the aggregated value for any device is changed
(for resume latency device PM QoS only).

int dev_pm_qos_remove_global_notifier(notifier):
Removes the notification callback function from the global notification tree
of the framework.


From user mode:
No API for user space access to the per-device latency constraints is provided
yet - still under discussion.
Active state latency tolerance

This device PM QoS type is used to support systems in which hardware may switch
to energy-saving operation modes on the fly.  In those systems, if the operation
mode chosen by the hardware attempts to save energy in an overly aggressive way,
it may cause excess latencies to be visible to software, causing it to miss
certain protocol requirements or target frame or sample rates etc.

If there is a latency tolerance control mechanism for a given device available
to software, the .set_latency_tolerance callback in that device's dev_pm_info
structure should be populated.  The routine pointed to by it is should implement
whatever is necessary to transfer the effective requirement value to the
hardware.

Whenever the effective latency tolerance changes for the device, its
.set_latency_tolerance() callback will be executed and the effective value will
be passed to it.  If that value is negative, which means that the list of
latency tolerance requirements for the device is empty, the callback is expected
to switch the underlying hardware latency tolerance control mechanism to an
autonomous mode if available.  If that value is PM_QOS_LATENCY_ANY, in turn, and
the hardware supports a special "no requirement" setting, the callback is
expected to use it.  That allows software to prevent the hardware from
automatically updating the device's latency tolerance in response to its power
state changes (e.g. during transitions from D3cold to D0), which generally may
be done in the autonomous latency tolerance control mode.

If .set_latency_tolerance() is present for the device, sysfs attribute
pm_qos_latency_tolerance_us will be present in the devivce's power directory.
Then, user space can use that attribute to specify its latency tolerance
requirement for the device, if any.  Writing "any" to it means "no requirement,
but do not let the hardware control latency tolerance" and writing "auto" to it
allows the hardware to be switched to the autonomous mode if there are no other
requirements from the kernel side in the device's list.

Kernel code can use the functions described above along with the
DEV_PM_QOS_LATENCY_TOLERANCE device PM QoS type to add, remove and update
latency tolerance requirements for devices.
+1 −1
Original line number Diff line number Diff line
@@ -92,5 +92,5 @@ dev_pm_qos_remove_request "device=%s type=%s new_value=%d"

The first parameter gives the device name which tries to add/update/remove
QoS requests.
The second parameter gives the request type (e.g. "DEV_PM_QOS_LATENCY").
The second parameter gives the request type (e.g. "DEV_PM_QOS_RESUME_LATENCY").
The third parameter is value to be added/updated/removed.
+70 −1
Original line number Diff line number Diff line
@@ -33,6 +33,13 @@ ACPI_MODULE_NAME("acpi_lpss");
#define LPSS_GENERAL_UART_RTS_OVRD	BIT(3)
#define LPSS_SW_LTR			0x10
#define LPSS_AUTO_LTR			0x14
#define LPSS_LTR_SNOOP_REQ		BIT(15)
#define LPSS_LTR_SNOOP_MASK		0x0000FFFF
#define LPSS_LTR_SNOOP_LAT_1US		0x800
#define LPSS_LTR_SNOOP_LAT_32US		0xC00
#define LPSS_LTR_SNOOP_LAT_SHIFT	5
#define LPSS_LTR_SNOOP_LAT_CUTOFF	3000
#define LPSS_LTR_MAX_VAL		0x3FF
#define LPSS_TX_INT			0x20
#define LPSS_TX_INT_MASK		BIT(1)

@@ -326,6 +333,17 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
	return ret;
}

static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
{
	return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
}

static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
			     unsigned int reg)
{
	writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
}

static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
{
	struct acpi_device *adev;
@@ -347,7 +365,7 @@ static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
		ret = -ENODEV;
		goto out;
	}
	*val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
	*val = __lpss_reg_read(pdata, reg);

 out:
	spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -400,6 +418,37 @@ static struct attribute_group lpss_attr_group = {
	.name = "lpss_ltr",
};

static void acpi_lpss_set_ltr(struct device *dev, s32 val)
{
	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
	u32 ltr_mode, ltr_val;

	ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
	if (val < 0) {
		if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
			ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
			__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
		}
		return;
	}
	ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
	if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
		ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
		val = LPSS_LTR_MAX_VAL;
	} else if (val > LPSS_LTR_MAX_VAL) {
		ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
		val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
	} else {
		ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
	}
	ltr_val |= val;
	__lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
	if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
		ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
		__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
	}
}

static int acpi_lpss_platform_notify(struct notifier_block *nb,
				     unsigned long action, void *data)
{
@@ -437,9 +486,29 @@ static struct notifier_block acpi_lpss_nb = {
	.notifier_call = acpi_lpss_platform_notify,
};

static void acpi_lpss_bind(struct device *dev)
{
	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));

	if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
		return;

	if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
		dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
	else
		dev_err(dev, "MMIO size insufficient to access LTR\n");
}

static void acpi_lpss_unbind(struct device *dev)
{
	dev->power.set_latency_tolerance = NULL;
}

static struct acpi_scan_handler lpss_handler = {
	.ids = acpi_lpss_device_ids,
	.attach = acpi_lpss_create_device,
	.bind = acpi_lpss_bind,
	.unbind = acpi_lpss_unbind,
};

void __init acpi_lpss_init(void)
+12 −0
Original line number Diff line number Diff line
@@ -287,6 +287,7 @@ EXPORT_SYMBOL_GPL(acpi_unbind_one);
static int acpi_platform_notify(struct device *dev)
{
	struct acpi_bus_type *type = acpi_get_bus_type(dev);
	struct acpi_device *adev;
	int ret;

	ret = acpi_bind_one(dev, NULL);
@@ -303,9 +304,14 @@ static int acpi_platform_notify(struct device *dev)
		if (ret)
			goto out;
	}
	adev = ACPI_COMPANION(dev);
	if (!adev)
		goto out;

	if (type && type->setup)
		type->setup(dev);
	else if (adev->handler && adev->handler->bind)
		adev->handler->bind(dev);

 out:
#if ACPI_GLUE_DEBUG
@@ -324,11 +330,17 @@ static int acpi_platform_notify(struct device *dev)

static int acpi_platform_notify_remove(struct device *dev)
{
	struct acpi_device *adev = ACPI_COMPANION(dev);
	struct acpi_bus_type *type;

	if (!adev)
		return 0;

	type = acpi_get_bus_type(dev);
	if (type && type->cleanup)
		type->cleanup(dev);
	else if (adev->handler && adev->handler->unbind)
		adev->handler->unbind(dev);

	acpi_unbind_one(dev);
	return 0;
Loading