/** * acpi_device_get_power - Get power state of an ACPI device. * @device: Device to get the power state of. * @state: Place to store the power state of the device. * * This function does not update the device's power.state field, but it may * update its parent's power.state field (when the parent's power state is * unknown and the device's power state turns out to be D0). */ int acpi_device_get_power(struct acpi_device *device, int *state) { int result = ACPI_STATE_UNKNOWN; if (!device || !state) return -EINVAL; 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; } /* * 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; } /* * If we were unsure about the device parent's power state up to this * point, the fact that the device is in D0 implies that the parent has * to be in D0 too. */ if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN && result == ACPI_STATE_D0) device->parent->power.state = ACPI_STATE_D0; *state = result; out: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", device->pnp.bus_id, acpi_power_state_string(*state))); return 0; }
/** * acpi_device_get_power - Get power state of an ACPI device. * @device: Device to get the power state of. * @state: Place to store the power state of the device. * * This function does not update the device's power.state field, but it may * update its parent's power.state field (when the parent's power state is * unknown and the device's power state turns out to be D0). */ int acpi_device_get_power(struct acpi_device *device, int *state) { int result = ACPI_STATE_UNKNOWN; if (!device || !state) return -EINVAL; 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; } /* * 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; 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; else if (result == ACPI_STATE_UNKNOWN) result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; } /* * If we were unsure about the device parent's power state up to this * point, the fact that the device is in D0 implies that the parent has * to be in D0 too, except if ignore_parent is set. */ if (!device->power.flags.ignore_parent && device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN && result == ACPI_STATE_D0) device->parent->power.state = ACPI_STATE_D0; *state = result; out: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", device->pnp.bus_id, acpi_power_state_string(*state))); return 0; }
/** * acpi_device_set_power - Set power state of an ACPI device. * @device: Device to set the power state of. * @state: New power state to set. * * Callers must ensure that the device is power manageable before using this * function. */ int acpi_device_set_power(struct acpi_device *device, int state) { int result = 0; bool cut_power = false; if (!device || !device->flags.power_manageable || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; /* Make sure this is a valid target state */ if (state == device->power.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already in %s\n", device->pnp.bus_id, acpi_power_state_string(state))); return 0; } if (!device->power.states[state].flags.valid) { dev_warn(&device->dev, "Power state %s not supported\n", acpi_power_state_string(state)); return -ENODEV; } if (!device->power.flags.ignore_parent && device->parent && (state < device->parent->power.state)) { dev_warn(&device->dev, "Cannot transition to power state %s for parent in %s\n", acpi_power_state_string(state), acpi_power_state_string(device->parent->power.state)); return -ENODEV; } /* For D3cold we should first transition into D3hot. */ if (state == ACPI_STATE_D3_COLD && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { state = ACPI_STATE_D3_HOT; cut_power = true; } if (state < device->power.state && state != ACPI_STATE_D0 && device->power.state >= ACPI_STATE_D3_HOT) { dev_warn(&device->dev, "Cannot transition to non-D0 state from D3\n"); return -ENODEV; } /* * Transition Power * ---------------- * In accordance with the ACPI specification first apply power (via * power resources) and then evaluate _PSx. */ if (device->power.flags.power_resources) { result = acpi_power_transition(device, state); if (result) goto end; } result = acpi_dev_pm_explicit_set(device, state); if (result) goto end; if (cut_power) { device->power.state = state; state = ACPI_STATE_D3_COLD; result = acpi_power_transition(device, state); } end: if (result) { dev_warn(&device->dev, "Failed to change power state to %s\n", acpi_power_state_string(state)); } else { device->power.state = state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to %s\n", device->pnp.bus_id, acpi_power_state_string(state))); } return result; }
/** * acpi_device_set_power - Set power state of an ACPI device. * @device: Device to set the power state of. * @state: New power state to set. * * Callers must ensure that the device is power manageable before using this * function. */ int acpi_device_set_power(struct acpi_device *device, int state) { int target_state = state; int result = 0; if (!device || !device->flags.power_manageable || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; /* Make sure this is a valid target state */ if (state == device->power.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already in %s\n", device->pnp.bus_id, acpi_power_state_string(state))); return 0; } if (state == ACPI_STATE_D3_COLD) { /* * For transitions to D3cold we need to execute _PS3 and then * possibly drop references to the power resources in use. */ state = ACPI_STATE_D3_HOT; /* If _PR3 is not available, use D3hot as the target state. */ if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid) target_state = state; } else if (!device->power.states[state].flags.valid) { dev_warn(&device->dev, "Power state %s not supported\n", acpi_power_state_string(state)); return -ENODEV; } if (!device->power.flags.ignore_parent && device->parent && (state < device->parent->power.state)) { dev_warn(&device->dev, "Cannot transition to power state %s for parent in %s\n", acpi_power_state_string(state), acpi_power_state_string(device->parent->power.state)); return -ENODEV; } /* * Transition Power * ---------------- * In accordance with ACPI 6, _PSx is executed before manipulating power * resources, unless the target state is D0, in which case _PS0 is * supposed to be executed after turning the power resources on. */ if (state > ACPI_STATE_D0) { /* * According to ACPI 6, devices cannot go from lower-power * (deeper) states to higher-power (shallower) states. */ if (state < device->power.state) { dev_warn(&device->dev, "Cannot transition from %s to %s\n", acpi_power_state_string(device->power.state), acpi_power_state_string(state)); return -ENODEV; } result = acpi_dev_pm_explicit_set(device, state); if (result) goto end; if (device->power.flags.power_resources) result = acpi_power_transition(device, target_state); } else { if (device->power.flags.power_resources) { result = acpi_power_transition(device, ACPI_STATE_D0); if (result) goto end; } result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); } end: if (result) { dev_warn(&device->dev, "Failed to change power state to %s\n", acpi_power_state_string(state)); } else { device->power.state = target_state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to %s\n", device->pnp.bus_id, acpi_power_state_string(state))); } return result; }
/** * acpi_device_set_power - Set power state of an ACPI device. * @device: Device to set the power state of. * @state: New power state to set. * * Callers must ensure that the device is power manageable before using this * function. */ int acpi_device_set_power(struct acpi_device *device, int state) { int result = 0; bool cut_power = false; if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; /* Make sure this is a valid target state */ if (state == device->power.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", acpi_power_state_string(state))); return 0; } if (!device->power.states[state].flags.valid) { printk(KERN_WARNING PREFIX "Device does not support %s\n", acpi_power_state_string(state)); return -ENODEV; } if (device->parent && (state < device->parent->power.state)) { printk(KERN_WARNING PREFIX "Cannot set device to a higher-powered" " state than parent\n"); return -ENODEV; } /* For D3cold we should first transition into D3hot. */ if (state == ACPI_STATE_D3_COLD && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { state = ACPI_STATE_D3_HOT; cut_power = true; } if (state < device->power.state && state != ACPI_STATE_D0 && device->power.state >= ACPI_STATE_D3_HOT) { printk(KERN_WARNING PREFIX "Cannot transition to non-D0 state from D3\n"); return -ENODEV; } /* * Transition Power * ---------------- * In accordance with the ACPI specification first apply power (via * power resources) and then evalute _PSx. */ if (device->power.flags.power_resources) { result = acpi_power_transition(device, state); if (result) goto end; } result = acpi_dev_pm_explicit_set(device, state); if (result) goto end; if (cut_power) { device->power.state = state; state = ACPI_STATE_D3_COLD; result = acpi_power_transition(device, state); } end: if (result) { printk(KERN_WARNING PREFIX "Device [%s] failed to transition to %s\n", device->pnp.bus_id, acpi_power_state_string(state)); } else { device->power.state = state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to %s\n", device->pnp.bus_id, acpi_power_state_string(state))); } return result; }