mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Merge branch 'pm-acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull some left-over PM patches from Rafael J. Wysocki. * 'pm-acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI / PM: Make acpi_pm_device_sleep_state() follow the specification ACPI / PM: Make __acpi_bus_get_power() cover D3cold correctly ACPI / PM: Fix error messages in drivers/acpi/bus.c rtc-cmos / PM: report wakeup event on ACPI RTC alarm ACPI / PM: Generate wakeup events on fixed power button
This commit is contained in:
		
						commit
						4d578573b8
					
				
					 5 changed files with 110 additions and 39 deletions
				
			
		| 
						 | 
				
			
			@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data);
 | 
			
		|||
                                 Power Management
 | 
			
		||||
   -------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
static const char *state_string(int state)
 | 
			
		||||
{
 | 
			
		||||
	switch (state) {
 | 
			
		||||
	case ACPI_STATE_D0:
 | 
			
		||||
		return "D0";
 | 
			
		||||
	case ACPI_STATE_D1:
 | 
			
		||||
		return "D1";
 | 
			
		||||
	case ACPI_STATE_D2:
 | 
			
		||||
		return "D2";
 | 
			
		||||
	case ACPI_STATE_D3_HOT:
 | 
			
		||||
		return "D3hot";
 | 
			
		||||
	case ACPI_STATE_D3_COLD:
 | 
			
		||||
		return "D3";
 | 
			
		||||
	default:
 | 
			
		||||
		return "(unknown)";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __acpi_bus_get_power(struct acpi_device *device, int *state)
 | 
			
		||||
{
 | 
			
		||||
	int result = 0;
 | 
			
		||||
	acpi_status status = 0;
 | 
			
		||||
	unsigned long long psc = 0;
 | 
			
		||||
	int result = ACPI_STATE_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
	if (!device || !state)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	*state = ACPI_STATE_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
	if (device->flags.power_manageable) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Get the device's power state either directly (via _PSC) or
 | 
			
		||||
		 * indirectly (via power resources).
 | 
			
		||||
		 */
 | 
			
		||||
		if (device->power.flags.power_resources) {
 | 
			
		||||
			result = acpi_power_get_inferred_state(device, state);
 | 
			
		||||
			if (result)
 | 
			
		||||
				return result;
 | 
			
		||||
		} else if (device->power.flags.explicit_get) {
 | 
			
		||||
			status = acpi_evaluate_integer(device->handle, "_PSC",
 | 
			
		||||
						       NULL, &psc);
 | 
			
		||||
			if (ACPI_FAILURE(status))
 | 
			
		||||
				return -ENODEV;
 | 
			
		||||
			*state = (int)psc;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
	if (!device->flags.power_manageable) {
 | 
			
		||||
		/* TBD: Non-recursive algorithm for walking up hierarchy. */
 | 
			
		||||
		*state = device->parent ?
 | 
			
		||||
			device->parent->power.state : ACPI_STATE_D0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n",
 | 
			
		||||
			  device->pnp.bus_id, *state));
 | 
			
		||||
	/*
 | 
			
		||||
	 * Get the device's power state either directly (via _PSC) or
 | 
			
		||||
	 * indirectly (via power resources).
 | 
			
		||||
	 */
 | 
			
		||||
	if (device->power.flags.explicit_get) {
 | 
			
		||||
		unsigned long long psc;
 | 
			
		||||
		acpi_status status = acpi_evaluate_integer(device->handle,
 | 
			
		||||
							   "_PSC", NULL, &psc);
 | 
			
		||||
		if (ACPI_FAILURE(status))
 | 
			
		||||
			return -ENODEV;
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
	*state = result;
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
 | 
			
		||||
			  device->pnp.bus_id, state_string(*state)));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
 | 
			
		|||
	/* Make sure this is a valid target state */
 | 
			
		||||
 | 
			
		||||
	if (state == device->power.state) {
 | 
			
		||||
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
 | 
			
		||||
				  state));
 | 
			
		||||
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
 | 
			
		||||
				  state_string(state)));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!device->power.states[state].flags.valid) {
 | 
			
		||||
		printk(KERN_WARNING PREFIX "Device does not support D%d\n", state);
 | 
			
		||||
		printk(KERN_WARNING PREFIX "Device does not support %s\n",
 | 
			
		||||
		       state_string(state));
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
	if (device->parent && (state < device->parent->power.state)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
 | 
			
		|||
      end:
 | 
			
		||||
	if (result)
 | 
			
		||||
		printk(KERN_WARNING PREFIX
 | 
			
		||||
			      "Device [%s] failed to transition to D%d\n",
 | 
			
		||||
			      device->pnp.bus_id, state);
 | 
			
		||||
			      "Device [%s] failed to transition to %s\n",
 | 
			
		||||
			      device->pnp.bus_id, state_string(state));
 | 
			
		||||
	else {
 | 
			
		||||
		device->power.state = state;
 | 
			
		||||
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 | 
			
		||||
				  "Device [%s] transitioned to D%d\n",
 | 
			
		||||
				  device->pnp.bus_id, state));
 | 
			
		||||
				  "Device [%s] transitioned to %s\n",
 | 
			
		||||
				  device->pnp.bus_id, state_string(state)));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
 | 
			
		|||
	 * We know a device's inferred power state when all the resources
 | 
			
		||||
	 * required for a given D-state are 'on'.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) {
 | 
			
		||||
	for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
 | 
			
		||||
		list = &device->power.states[i].resources;
 | 
			
		||||
		if (list->count < 1)
 | 
			
		||||
			continue;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1567,6 +1567,7 @@ static int acpi_bus_scan_fixed(void)
 | 
			
		|||
						ACPI_BUS_TYPE_POWER_BUTTON,
 | 
			
		||||
						ACPI_STA_DEFAULT,
 | 
			
		||||
						&ops);
 | 
			
		||||
		device_init_wakeup(&device->dev, true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
 | 
			
		|||
MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
 | 
			
		||||
 | 
			
		||||
static u8 sleep_states[ACPI_S_STATE_COUNT];
 | 
			
		||||
static bool pwr_btn_event_pending;
 | 
			
		||||
 | 
			
		||||
static void acpi_sleep_tts_switch(u32 acpi_state)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -184,6 +185,14 @@ static int acpi_pm_prepare(void)
 | 
			
		|||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int find_powerf_dev(struct device *dev, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_device *device = to_acpi_device(dev);
 | 
			
		||||
	const char *hid = acpi_device_hid(device);
 | 
			
		||||
 | 
			
		||||
	return !strcmp(hid, ACPI_BUTTON_HID_POWERF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	acpi_pm_finish - Instruct the platform to leave a sleep state.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +201,7 @@ static int acpi_pm_prepare(void)
 | 
			
		|||
 */
 | 
			
		||||
static void acpi_pm_finish(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device *pwr_btn_dev;
 | 
			
		||||
	u32 acpi_state = acpi_target_sleep_state;
 | 
			
		||||
 | 
			
		||||
	acpi_ec_unblock_transactions();
 | 
			
		||||
| 
						 | 
				
			
			@ -209,6 +219,23 @@ static void acpi_pm_finish(void)
 | 
			
		|||
	acpi_set_firmware_waking_vector((acpi_physical_address) 0);
 | 
			
		||||
 | 
			
		||||
	acpi_target_sleep_state = ACPI_STATE_S0;
 | 
			
		||||
 | 
			
		||||
	/* If we were woken with the fixed power button, provide a small
 | 
			
		||||
	 * hint to userspace in the form of a wakeup event on the fixed power
 | 
			
		||||
	 * button device (if it can be found).
 | 
			
		||||
	 *
 | 
			
		||||
	 * We delay the event generation til now, as the PM layer requires
 | 
			
		||||
	 * timekeeping to be running before we generate events. */
 | 
			
		||||
	if (!pwr_btn_event_pending)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pwr_btn_event_pending = false;
 | 
			
		||||
	pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL,
 | 
			
		||||
				      find_powerf_dev);
 | 
			
		||||
	if (pwr_btn_dev) {
 | 
			
		||||
		pm_wakeup_event(pwr_btn_dev, 0);
 | 
			
		||||
		put_device(pwr_btn_dev);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -298,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
 | 
			
		|||
	/* ACPI 3.0 specs (P62) says that it's the responsibility
 | 
			
		||||
	 * of the OSPM to clear the status bit [ implying that the
 | 
			
		||||
	 * POWER_BUTTON event should not reach userspace ]
 | 
			
		||||
	 *
 | 
			
		||||
	 * However, we do generate a small hint for userspace in the form of
 | 
			
		||||
	 * a wakeup event. We flag this condition for now and generate the
 | 
			
		||||
	 * event later, as we're currently too early in resume to be able to
 | 
			
		||||
	 * generate wakeup events.
 | 
			
		||||
	 */
 | 
			
		||||
	if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
 | 
			
		||||
		acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
 | 
			
		||||
	if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
 | 
			
		||||
		acpi_event_status pwr_btn_status;
 | 
			
		||||
 | 
			
		||||
		acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
 | 
			
		||||
 | 
			
		||||
		if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
 | 
			
		||||
			acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
 | 
			
		||||
			/* Flag for later */
 | 
			
		||||
			pwr_btn_event_pending = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Disable and clear GPE status before interrupt is enabled. Some GPEs
 | 
			
		||||
| 
						 | 
				
			
			@ -730,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
 | 
			
		|||
	 * can wake the system.  _S0W may be valid, too.
 | 
			
		||||
	 */
 | 
			
		||||
	if (acpi_target_sleep_state == ACPI_STATE_S0 ||
 | 
			
		||||
	    (device_may_wakeup(dev) &&
 | 
			
		||||
	     adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
 | 
			
		||||
	    (device_may_wakeup(dev) && adev->wakeup.flags.valid &&
 | 
			
		||||
	     adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
 | 
			
		||||
		acpi_status status;
 | 
			
		||||
 | 
			
		||||
		acpi_method[3] = 'W';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -910,14 +910,17 @@ static inline int cmos_poweroff(struct device *dev)
 | 
			
		|||
 | 
			
		||||
static u32 rtc_handler(void *context)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = context;
 | 
			
		||||
 | 
			
		||||
	pm_wakeup_event(dev, 0);
 | 
			
		||||
	acpi_clear_event(ACPI_EVENT_RTC);
 | 
			
		||||
	acpi_disable_event(ACPI_EVENT_RTC, 0);
 | 
			
		||||
	return ACPI_INTERRUPT_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void rtc_wake_setup(void)
 | 
			
		||||
static inline void rtc_wake_setup(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
 | 
			
		||||
	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev);
 | 
			
		||||
	/*
 | 
			
		||||
	 * After the RTC handler is installed, the Fixed_RTC event should
 | 
			
		||||
	 * be disabled. Only when the RTC alarm is set will it be enabled.
 | 
			
		||||
| 
						 | 
				
			
			@ -950,7 +953,7 @@ cmos_wake_setup(struct device *dev)
 | 
			
		|||
	if (acpi_disabled)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	rtc_wake_setup();
 | 
			
		||||
	rtc_wake_setup(dev);
 | 
			
		||||
	acpi_rtc_info.wake_on = rtc_wake_on;
 | 
			
		||||
	acpi_rtc_info.wake_off = rtc_wake_off;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue