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

Commit 34bdb1a4 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-pm'

* acpi-pm:
  ACPI / PM: Expose lists of device wakeup power resources to user space
  ACPI / PM: Fix potential problem in acpi_device_get_power()
parents 0ad4991c 41a2a466
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
What:		/sys/devices/.../power_resources_wakeup/
Date:		April 2013
Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
		The /sys/devices/.../power_resources_wakeup/ directory is only
		present for device objects representing ACPI device nodes that
		require ACPI power resources for wakeup signaling.

		If present, it contains symbolic links to device directories
		representing ACPI power resources that need to be turned on for
		the given device node to be able to signal wakeup.  The names of
		the links are the same as the names of the directories they
		point to.
+24 −15
Original line number Diff line number Diff line
@@ -145,27 +145,36 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
	}

	/*
	 * Get the device's power state either directly (via _PSC) or
	 * indirectly (via power resources).
	 * Get the device's power state from power resources settings and _PSC,
	 * if available.
	 */
	if (device->power.flags.power_resources) {
		int error = acpi_power_get_inferred_state(device, &result);
		if (error)
			return error;
	}
	if (device->power.flags.explicit_get) {
		acpi_handle handle = device->handle;
		unsigned long long psc;
		acpi_status status = acpi_evaluate_integer(device->handle,
							   "_PSC", NULL, &psc);
		acpi_status status;

		status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc);
		if (ACPI_FAILURE(status))
			return -ENODEV;

		/*
		 * The power resources settings may indicate a power state
		 * shallower than the actual power state of the device.
		 *
		 * Moreover, on systems predating ACPI 4.0, if the device
		 * doesn't depend on any power resources and _PSC returns 3,
		 * that means "power off".  We need to maintain compatibility
		 * with those systems.
		 */
		if (psc > result && psc < ACPI_STATE_D3_COLD)
			result = psc;
	}
	/* The test below covers ACPI_STATE_UNKNOWN too. */
	if (result <= ACPI_STATE_D2) {
	  ; /* Do nothing. */
	} else if (device->power.flags.power_resources) {
		int error = acpi_power_get_inferred_state(device, &result);
		if (error)
			return error;
	} else if (result == ACPI_STATE_D3_HOT) {
		result = ACPI_STATE_D3;
		else if (result == ACPI_STATE_UNKNOWN)
			result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc;
	}

	/*
+39 −19
Original line number Diff line number Diff line
@@ -459,57 +459,79 @@ static struct attribute_group attr_groups[] = {
	},
};

static void acpi_power_hide_list(struct acpi_device *adev, int state)
static struct attribute_group wakeup_attr_group = {
	.name = "power_resources_wakeup",
	.attrs = attrs,
};

static void acpi_power_hide_list(struct acpi_device *adev,
				 struct list_head *resources,
				 struct attribute_group *attr_group)
{
	struct acpi_device_power_state *ps = &adev->power.states[state];
	struct acpi_power_resource_entry *entry;

	if (list_empty(&ps->resources))
	if (list_empty(resources))
		return;

	list_for_each_entry_reverse(entry, &ps->resources, node) {
	list_for_each_entry_reverse(entry, resources, node) {
		struct acpi_device *res_dev = &entry->resource->device;

		sysfs_remove_link_from_group(&adev->dev.kobj,
					     attr_groups[state].name,
					     attr_group->name,
					     dev_name(&res_dev->dev));
	}
	sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]);
	sysfs_remove_group(&adev->dev.kobj, attr_group);
}

static void acpi_power_expose_list(struct acpi_device *adev, int state)
static void acpi_power_expose_list(struct acpi_device *adev,
				   struct list_head *resources,
				   struct attribute_group *attr_group)
{
	struct acpi_device_power_state *ps = &adev->power.states[state];
	struct acpi_power_resource_entry *entry;
	int ret;

	if (list_empty(&ps->resources))
	if (list_empty(resources))
		return;

	ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]);
	ret = sysfs_create_group(&adev->dev.kobj, attr_group);
	if (ret)
		return;

	list_for_each_entry(entry, &ps->resources, node) {
	list_for_each_entry(entry, resources, node) {
		struct acpi_device *res_dev = &entry->resource->device;

		ret = sysfs_add_link_to_group(&adev->dev.kobj,
					      attr_groups[state].name,
					      attr_group->name,
					      &res_dev->dev.kobj,
					      dev_name(&res_dev->dev));
		if (ret) {
			acpi_power_hide_list(adev, state);
			acpi_power_hide_list(adev, resources, attr_group);
			break;
		}
	}
}

static void acpi_power_expose_hide(struct acpi_device *adev,
				   struct list_head *resources,
				   struct attribute_group *attr_group,
				   bool expose)
{
	if (expose)
		acpi_power_expose_list(adev, resources, attr_group);
	else
		acpi_power_hide_list(adev, resources, attr_group);
}

void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
{
	struct acpi_device_power_state *ps;
	struct acpi_power_resource_entry *entry;
	int state;

	if (adev->wakeup.flags.valid)
		acpi_power_expose_hide(adev, &adev->wakeup.resources,
				       &wakeup_attr_group, add);

	if (!adev->power.flags.power_resources)
		return;

@@ -523,12 +545,10 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
			acpi_power_remove_dependent(resource, adev);
	}

	for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) {
		if (add)
			acpi_power_expose_list(adev, state);
		else
			acpi_power_hide_list(adev, state);
	}
	for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++)
		acpi_power_expose_hide(adev,
				       &adev->power.states[state].resources,
				       &attr_groups[state], add);
}

int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)