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

Commit 99d8845e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

ACPI / PM: Split acpi_device_wakeup()



To prepare for a subsequent change and make the code somewhat easier
to follow, do the following in the ACPI device wakeup handling code:

 * Replace wakeup.flags.enabled under struct acpi_device with
   wakeup.enable_count as that will be necessary going forward.

   For now, wakeup.enable_count is not allowed to grow beyond 1,
   so the current behavior is retained.

 * Split acpi_device_wakeup() into acpi_device_wakeup_enable()
   and acpi_device_wakeup_disable() and modify the callers of
   it accordingly.

 * Introduce a new acpi_wakeup_lock mutex to protect the wakeup
   enabling/disabling code from races in case it is executed
   more than once in parallel for the same device (which may
   happen for bridges theoretically).

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent baecc470
Loading
Loading
Loading
Loading
+79 −42
Original line number Original line Diff line number Diff line
@@ -682,47 +682,74 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
	}
	}
}
}


static DEFINE_MUTEX(acpi_wakeup_lock);

/**
/**
 * acpi_device_wakeup - Enable/disable wakeup functionality for device.
 * acpi_device_wakeup_enable - Enable wakeup functionality for device.
 * @adev: ACPI device to enable/disable wakeup functionality for.
 * @adev: ACPI device to enable wakeup functionality for.
 * @target_state: State the system is transitioning into.
 * @target_state: State the system is transitioning into.
 * @enable: Whether to enable or disable the wakeup functionality.
 *
 *
 * Enable/disable the GPE associated with @adev so that it can generate
 * Enable the GPE associated with @adev so that it can generate wakeup signals
 * wakeup signals for the device in response to external (remote) events and
 * for the device in response to external (remote) events and enable wakeup
 * enable/disable device wakeup power.
 * power for it.
 *
 *
 * Callers must ensure that @adev is a valid ACPI device node before executing
 * Callers must ensure that @adev is a valid ACPI device node before executing
 * this function.
 * this function.
 */
 */
static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state,
static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
			      bool enable)
{
{
	struct acpi_device_wakeup *wakeup = &adev->wakeup;
	struct acpi_device_wakeup *wakeup = &adev->wakeup;
	acpi_status status;
	int error = 0;


	if (enable) {
	mutex_lock(&acpi_wakeup_lock);
		acpi_status res;
		int error;


		if (adev->wakeup.flags.enabled)
	if (wakeup->enable_count > 0)
			return 0;
		goto out;


	error = acpi_enable_wakeup_device_power(adev, target_state);
	error = acpi_enable_wakeup_device_power(adev, target_state);
	if (error)
	if (error)
			return error;
		goto out;


		res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
	status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
		if (ACPI_FAILURE(res)) {
	if (ACPI_FAILURE(status)) {
		acpi_disable_wakeup_device_power(adev);
		acpi_disable_wakeup_device_power(adev);
			return -EIO;
		error = -EIO;
		goto out;
	}
	}
		adev->wakeup.flags.enabled = 1;

	} else if (adev->wakeup.flags.enabled) {
	wakeup->enable_count++;

out:
	mutex_unlock(&acpi_wakeup_lock);
	return error;
}

/**
 * acpi_device_wakeup_disable - Disable wakeup functionality for device.
 * @adev: ACPI device to disable wakeup functionality for.
 *
 * Disable the GPE associated with @adev and disable wakeup power for it.
 *
 * Callers must ensure that @adev is a valid ACPI device node before executing
 * this function.
 */
static void acpi_device_wakeup_disable(struct acpi_device *adev)
{
	struct acpi_device_wakeup *wakeup = &adev->wakeup;

	mutex_lock(&acpi_wakeup_lock);

	if (!wakeup->enable_count)
		goto out;

	acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
	acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
	acpi_disable_wakeup_device_power(adev);
	acpi_disable_wakeup_device_power(adev);
		adev->wakeup.flags.enabled = 0;

	}
	wakeup->enable_count--;
	return 0;

out:
	mutex_unlock(&acpi_wakeup_lock);
}
}


/**
/**
@@ -744,9 +771,15 @@ int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
	if (!acpi_device_can_wakeup(adev))
	if (!acpi_device_can_wakeup(adev))
		return -EINVAL;
		return -EINVAL;


	error = acpi_device_wakeup(adev, acpi_target_system_state(), enable);
	if (!enable) {
		acpi_device_wakeup_disable(adev);
		dev_dbg(dev, "Wakeup disabled by ACPI\n");
		return 0;
	}

	error = acpi_device_wakeup_enable(adev, acpi_target_system_state());
	if (!error)
	if (!error)
		dev_dbg(dev, "Wakeup %s by ACPI\n", enable ? "enabled" : "disabled");
		dev_dbg(dev, "Wakeup enabled by ACPI\n");


	return error;
	return error;
}
}
@@ -800,13 +833,15 @@ int acpi_dev_runtime_suspend(struct device *dev)


	remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
	remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
				PM_QOS_FLAGS_NONE;
				PM_QOS_FLAGS_NONE;
	error = acpi_device_wakeup(adev, ACPI_STATE_S0, remote_wakeup);
	if (remote_wakeup) {
	if (remote_wakeup && error)
		error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
		if (error)
			return -EAGAIN;
			return -EAGAIN;
	}


	error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
	error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
	if (error)
	if (error && remote_wakeup)
		acpi_device_wakeup(adev, ACPI_STATE_S0, false);
		acpi_device_wakeup_disable(adev);


	return error;
	return error;
}
}
@@ -829,7 +864,7 @@ int acpi_dev_runtime_resume(struct device *dev)
		return 0;
		return 0;


	error = acpi_dev_pm_full_power(adev);
	error = acpi_dev_pm_full_power(adev);
	acpi_device_wakeup(adev, ACPI_STATE_S0, false);
	acpi_device_wakeup_disable(adev);
	return error;
	return error;
}
}
EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
@@ -884,13 +919,15 @@ int acpi_dev_suspend_late(struct device *dev)


	target_state = acpi_target_system_state();
	target_state = acpi_target_system_state();
	wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
	wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
	error = acpi_device_wakeup(adev, target_state, wakeup);
	if (wakeup) {
	if (wakeup && error)
		error = acpi_device_wakeup_enable(adev, target_state);
		if (error)
			return error;
			return error;
	}


	error = acpi_dev_pm_low_power(dev, adev, target_state);
	error = acpi_dev_pm_low_power(dev, adev, target_state);
	if (error)
	if (error && wakeup)
		acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
		acpi_device_wakeup_disable(adev);


	return error;
	return error;
}
}
@@ -913,7 +950,7 @@ int acpi_dev_resume_early(struct device *dev)
		return 0;
		return 0;


	error = acpi_dev_pm_full_power(adev);
	error = acpi_dev_pm_full_power(adev);
	acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
	acpi_device_wakeup_disable(adev);
	return error;
	return error;
}
}
EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
@@ -1056,7 +1093,7 @@ static void acpi_dev_pm_detach(struct device *dev, bool power_off)
			 */
			 */
			dev_pm_qos_hide_latency_limit(dev);
			dev_pm_qos_hide_latency_limit(dev);
			dev_pm_qos_hide_flags(dev);
			dev_pm_qos_hide_flags(dev);
			acpi_device_wakeup(adev, ACPI_STATE_S0, false);
			acpi_device_wakeup_disable(adev);
			acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
			acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
		}
		}
	}
	}
@@ -1100,7 +1137,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
	dev_pm_domain_set(dev, &acpi_general_pm_domain);
	dev_pm_domain_set(dev, &acpi_general_pm_domain);
	if (power_on) {
	if (power_on) {
		acpi_dev_pm_full_power(adev);
		acpi_dev_pm_full_power(adev);
		acpi_device_wakeup(adev, ACPI_STATE_S0, false);
		acpi_device_wakeup_disable(adev);
	}
	}


	dev->pm_domain->detach = acpi_dev_pm_detach;
	dev->pm_domain->detach = acpi_dev_pm_detach;
+1 −1
Original line number Original line Diff line number Diff line
@@ -316,7 +316,6 @@ struct acpi_device_perf {
struct acpi_device_wakeup_flags {
struct acpi_device_wakeup_flags {
	u8 valid:1;		/* Can successfully enable wakeup? */
	u8 valid:1;		/* Can successfully enable wakeup? */
	u8 notifier_present:1;  /* Wake-up notify handler has been installed */
	u8 notifier_present:1;  /* Wake-up notify handler has been installed */
	u8 enabled:1;		/* Enabled for wakeup */
};
};


struct acpi_device_wakeup_context {
struct acpi_device_wakeup_context {
@@ -333,6 +332,7 @@ struct acpi_device_wakeup {
	struct acpi_device_wakeup_context context;
	struct acpi_device_wakeup_context context;
	struct wakeup_source *ws;
	struct wakeup_source *ws;
	int prepare_count;
	int prepare_count;
	int enable_count;
};
};


struct acpi_device_physical_node {
struct acpi_device_physical_node {