Commit 4e03e4e6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull power management fixes from Rafael Wysocki:
 "Fix three issues related to the handling of wakeup events signaled
  through the ACPI SCI while suspended to idle (Rafael Wysocki) and
  unexport an internal cpufreq variable (Yangtao Li)"

* tag 'pm-5.6-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI: PM: s2idle: Prevent spurious SCIs from waking up the system
  ACPICA: Introduce acpi_any_gpe_status_set()
  ACPI: PM: s2idle: Avoid possible race related to the EC GPE
  ACPI: EC: Fix flushing of pending work
  cpufreq: Make cpufreq_global_kobject static
parents 81f3011c 3629ac5b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -101,6 +101,8 @@ acpi_status acpi_hw_enable_all_runtime_gpes(void);

acpi_status acpi_hw_enable_all_wakeup_gpes(void);

u8 acpi_hw_check_all_gpes(void);

acpi_status
acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
				 struct acpi_gpe_block_info *gpe_block,
+32 −0
Original line number Diff line number Diff line
@@ -795,6 +795,38 @@ acpi_status acpi_enable_all_wakeup_gpes(void)

ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)

/******************************************************************************
 *
 * FUNCTION:    acpi_any_gpe_status_set
 *
 * PARAMETERS:  None
 *
 * RETURN:      Whether or not the status bit is set for any GPE
 *
 * DESCRIPTION: Check the status bits of all enabled GPEs and return TRUE if any
 *              of them is set or FALSE otherwise.
 *
 ******************************************************************************/
u32 acpi_any_gpe_status_set(void)
{
	acpi_status status;
	u8 ret;

	ACPI_FUNCTION_TRACE(acpi_any_gpe_status_set);

	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
	if (ACPI_FAILURE(status)) {
		return (FALSE);
	}

	ret = acpi_hw_check_all_gpes();
	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);

	return (ret);
}

ACPI_EXPORT_SYMBOL(acpi_any_gpe_status_set)

/*******************************************************************************
 *
 * FUNCTION:    acpi_install_gpe_block
+71 −0
Original line number Diff line number Diff line
@@ -444,6 +444,53 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
	return (AE_OK);
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_get_gpe_block_status
 *
 * PARAMETERS:  gpe_xrupt_info      - GPE Interrupt info
 *              gpe_block           - Gpe Block info
 *
 * RETURN:      Success
 *
 * DESCRIPTION: Produce a combined GPE status bits mask for the given block.
 *
 ******************************************************************************/

static acpi_status
acpi_hw_get_gpe_block_status(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
			     struct acpi_gpe_block_info *gpe_block,
			     void *ret_ptr)
{
	struct acpi_gpe_register_info *gpe_register_info;
	u64 in_enable, in_status;
	acpi_status status;
	u8 *ret = ret_ptr;
	u32 i;

	/* Examine each GPE Register within the block */

	for (i = 0; i < gpe_block->register_count; i++) {
		gpe_register_info = &gpe_block->register_info[i];

		status = acpi_hw_read(&in_enable,
				      &gpe_register_info->enable_address);
		if (ACPI_FAILURE(status)) {
			continue;
		}

		status = acpi_hw_read(&in_status,
				      &gpe_register_info->status_address);
		if (ACPI_FAILURE(status)) {
			continue;
		}

		*ret |= in_enable & in_status;
	}

	return (AE_OK);
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_disable_all_gpes
@@ -510,4 +557,28 @@ acpi_status acpi_hw_enable_all_wakeup_gpes(void)
	return_ACPI_STATUS(status);
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_check_all_gpes
 *
 * PARAMETERS:  None
 *
 * RETURN:      Combined status of all GPEs
 *
 * DESCRIPTION: Check all enabled GPEs in all GPE blocks and return TRUE if the
 *              status bit is set for at least one of them of FALSE otherwise.
 *
 ******************************************************************************/

u8 acpi_hw_check_all_gpes(void)
{
	u8 ret = 0;

	ACPI_FUNCTION_TRACE(acpi_hw_check_all_gpes);

	(void)acpi_ev_walk_gpe_list(acpi_hw_get_gpe_block_status, &ret);

	return (ret != 0);
}

#endif				/* !ACPI_REDUCED_HARDWARE */
+26 −18
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ EXPORT_SYMBOL(first_ec);

static struct acpi_ec *boot_ec;
static bool boot_ec_is_ecdt = false;
static struct workqueue_struct *ec_wq;
static struct workqueue_struct *ec_query_wq;

static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */
@@ -469,7 +470,7 @@ static void acpi_ec_submit_query(struct acpi_ec *ec)
		ec_dbg_evt("Command(%s) submitted/blocked",
			   acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
		ec->nr_pending_queries++;
		schedule_work(&ec->work);
		queue_work(ec_wq, &ec->work);
	}
}

@@ -535,7 +536,7 @@ static void acpi_ec_enable_event(struct acpi_ec *ec)
#ifdef CONFIG_PM_SLEEP
static void __acpi_ec_flush_work(void)
{
	flush_scheduled_work(); /* flush ec->work */
	drain_workqueue(ec_wq); /* flush ec->work */
	flush_workqueue(ec_query_wq); /* flush queries */
}

@@ -556,8 +557,8 @@ static void acpi_ec_disable_event(struct acpi_ec *ec)

void acpi_ec_flush_work(void)
{
	/* Without ec_query_wq there is nothing to flush. */
	if (!ec_query_wq)
	/* Without ec_wq there is nothing to flush. */
	if (!ec_wq)
		return;

	__acpi_ec_flush_work();
@@ -2107,25 +2108,33 @@ static struct acpi_driver acpi_ec_driver = {
	.drv.pm = &acpi_ec_pm,
};

static inline int acpi_ec_query_init(void)
static void acpi_ec_destroy_workqueues(void)
{
	if (!ec_query_wq) {
		ec_query_wq = alloc_workqueue("kec_query", 0,
					      ec_max_queries);
		if (!ec_query_wq)
			return -ENODEV;
	}
	return 0;
	if (ec_wq) {
		destroy_workqueue(ec_wq);
		ec_wq = NULL;
	}

static inline void acpi_ec_query_exit(void)
{
	if (ec_query_wq) {
		destroy_workqueue(ec_query_wq);
		ec_query_wq = NULL;
	}
}

static int acpi_ec_init_workqueues(void)
{
	if (!ec_wq)
		ec_wq = alloc_ordered_workqueue("kec", 0);

	if (!ec_query_wq)
		ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries);

	if (!ec_wq || !ec_query_wq) {
		acpi_ec_destroy_workqueues();
		return -ENODEV;
	}
	return 0;
}

static const struct dmi_system_id acpi_ec_no_wakeup[] = {
	{
		.ident = "Thinkpad X1 Carbon 6th",
@@ -2156,8 +2165,7 @@ int __init acpi_ec_init(void)
	int result;
	int ecdt_fail, dsdt_fail;

	/* register workqueue for _Qxx evaluations */
	result = acpi_ec_query_init();
	result = acpi_ec_init_workqueues();
	if (result)
		return result;

@@ -2188,6 +2196,6 @@ static void __exit acpi_ec_exit(void)
{

	acpi_bus_unregister_driver(&acpi_ec_driver);
	acpi_ec_query_exit();
	acpi_ec_destroy_workqueues();
}
#endif	/* 0 */
+37 −13
Original line number Diff line number Diff line
@@ -990,21 +990,34 @@ static void acpi_s2idle_sync(void)
	acpi_os_wait_events_complete(); /* synchronize Notify handling */
}

static void acpi_s2idle_wake(void)
static bool acpi_s2idle_wake(void)
{
	if (!acpi_sci_irq_valid())
		return pm_wakeup_pending();

	while (pm_wakeup_pending()) {
		/*
	 * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has
	 * not triggered while suspended, so bail out.
		 * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the
		 * SCI has not triggered while suspended, so bail out (the
		 * wakeup is pending anyway and the SCI is not the source of
		 * it).
		 */
	if (!acpi_sci_irq_valid() ||
	    irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
		return;
		if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
			return true;

		/*
	 * If there are EC events to process, the wakeup may be a spurious one
	 * coming from the EC.
		 * If there are no EC events to process and at least one of the
		 * other enabled GPEs is active, the wakeup is regarded as a
		 * genuine one.
		 *
		 * Note that the checks below must be carried out in this order
		 * to avoid returning prematurely due to a change of the EC GPE
		 * status bit from unset to set between the checks with the
		 * status bits of all the other GPEs unset.
		 */
	if (acpi_ec_dispatch_gpe()) {
		if (acpi_any_gpe_status_set() && !acpi_ec_dispatch_gpe())
			return true;

		/*
		 * Cancel the wakeup and process all pending events in case
		 * there are any wakeup ones in there.
@@ -1017,8 +1030,19 @@ static void acpi_s2idle_wake(void)

		acpi_s2idle_sync();

		/*
		 * The SCI is in the "suspended" state now and it cannot produce
		 * new wakeup events till the rearming below, so if any of them
		 * are pending here, they must be resulting from the processing
		 * of EC events above or coming from somewhere else.
		 */
		if (pm_wakeup_pending())
			return true;

		rearm_wake_irq(acpi_sci_irq);
	}

	return false;
}

static void acpi_s2idle_restore_early(void)
Loading