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

Commit 2af52c2b authored by Lv Zheng's avatar Lv Zheng Committed by Rafael J. Wysocki
Browse files

ACPICA: Events: Introduce acpi_mask_gpe() to implement GPE masking mechanism

ACPICA commit 23a417ca406a527e7ae1710893e59a8b6db30e14

There is a facility in Linux, developers can control the enabling/disabling
of a GPE via /sys/firmware/acpi/interrupts/gpexx. This is mainly for
debugging purposes.

But many users expect to use this facility to implement quirks to mask a
specific GPE when there is a gap in Linux causing this GPE to flood. This
is not working correctly because currently this facility invokes
enabling/disabling counting based GPE driver APIs:
 acpi_enable_gpe()/acpi_disable_gpe()
and the GPE drivers can still affect the count to mess up the GPE
masking purposes.

However, most of the IRQ chip designs allow masking/unmasking IRQs via a
masking bit which is different from the enabled bit to achieve the same
purpose. But the GPE hardware doesn't contain such a feature, this brings
the trouble.

In this patch, we introduce a software mechanism to implement the GPE
masking feature, and acpi_mask_gpe() are provided to the OSPMs to
mask/unmask GPEs in the above mentioned situation instead of
acpi_enable_gpe()/acpi_disable_gpe(). ACPICA BZ 1102. Lv Zheng.

Link: https://github.com/acpica/acpica/commit/23a417ca
Link: https://bugs.acpica.org/show_bug.cgi?id=1102


Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 9556ec4e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -85,6 +85,9 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info);

acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);

acpi_status
acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked);

acpi_status
acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info);

+2 −0
Original line number Diff line number Diff line
@@ -484,6 +484,7 @@ struct acpi_gpe_event_info {
	u8 flags;		/* Misc info about this GPE */
	u8 gpe_number;		/* This GPE */
	u8 runtime_count;	/* References to a run GPE */
	u8 disable_for_dispatch;	/* Masked during dispatching */
};

/* Information about a GPE register pair, one per each status/enable pair in an array */
@@ -494,6 +495,7 @@ struct acpi_gpe_register_info {
	u16 base_gpe_number;	/* Base GPE number for this register */
	u8 enable_for_wake;	/* GPEs to keep enabled when sleeping */
	u8 enable_for_run;	/* GPEs to keep enabled when running */
	u8 mask_for_run;	/* GPEs to keep masked when running */
	u8 enable_mask;		/* Current mask of enabled GPEs */
};

+57 −0
Original line number Diff line number Diff line
@@ -128,6 +128,60 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
	return_ACPI_STATUS(status);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_mask_gpe
 *
 * PARAMETERS:  gpe_event_info          - GPE to be blocked/unblocked
 *              is_masked               - Whether the GPE is masked or not
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Unconditionally mask/unmask a GPE during runtime.
 *
 ******************************************************************************/

acpi_status
acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked)
{
	struct acpi_gpe_register_info *gpe_register_info;
	u32 register_bit;

	ACPI_FUNCTION_TRACE(ev_mask_gpe);

	gpe_register_info = gpe_event_info->register_info;
	if (!gpe_register_info) {
		return_ACPI_STATUS(AE_NOT_EXIST);
	}

	register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);

	/* Perform the action */

	if (is_masked) {
		if (register_bit & gpe_register_info->mask_for_run) {
			return_ACPI_STATUS(AE_BAD_PARAMETER);
		}

		(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
		ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit);
	} else {
		if (!(register_bit & gpe_register_info->mask_for_run)) {
			return_ACPI_STATUS(AE_BAD_PARAMETER);
		}

		ACPI_CLEAR_BIT(gpe_register_info->mask_for_run,
			       (u8)register_bit);
		if (gpe_event_info->runtime_count
		    && !gpe_event_info->disable_for_dispatch) {
			(void)acpi_hw_low_set_gpe(gpe_event_info,
						  ACPI_GPE_ENABLE);
		}
	}

	return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_add_gpe_reference
@@ -674,6 +728,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
	 * in the event_info.
	 */
	(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE);
	gpe_event_info->disable_for_dispatch = FALSE;
	return (AE_OK);
}

@@ -737,6 +792,8 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
		}
	}

	gpe_event_info->disable_for_dispatch = TRUE;

	/*
	 * Dispatch the GPE to either an installed handler or the control
	 * method associated with this GPE (_Lxx or _Exx). If a handler
+43 −0
Original line number Diff line number Diff line
@@ -235,11 +235,13 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
	case ACPI_GPE_ENABLE:

		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
		gpe_event_info->disable_for_dispatch = FALSE;
		break;

	case ACPI_GPE_DISABLE:

		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
		gpe_event_info->disable_for_dispatch = TRUE;
		break;

	default:
@@ -255,6 +257,47 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)

ACPI_EXPORT_SYMBOL(acpi_set_gpe)

/*******************************************************************************
 *
 * FUNCTION:    acpi_mask_gpe
 *
 * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
 *              gpe_number          - GPE level within the GPE block
 *              is_masked           - Whether the GPE is masked or not
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to
 *              prevent a GPE flooding.
 *
 ******************************************************************************/
acpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked)
{
	struct acpi_gpe_event_info *gpe_event_info;
	acpi_status status;
	acpi_cpu_flags flags;

	ACPI_FUNCTION_TRACE(acpi_mask_gpe);

	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);

	/* Ensure that we have a valid GPE number */

	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
	if (!gpe_event_info) {
		status = AE_BAD_PARAMETER;
		goto unlock_and_exit;
	}

	status = acpi_ev_mask_gpe(gpe_event_info, is_masked);

unlock_and_exit:
	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
	return_ACPI_STATUS(status);
}

ACPI_EXPORT_SYMBOL(acpi_mask_gpe)

/*******************************************************************************
 *
 * FUNCTION:    acpi_mark_gpe_for_wake
+18 −5
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ acpi_status
acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
{
	struct acpi_gpe_register_info *gpe_register_info;
	acpi_status status;
	acpi_status status = AE_OK;
	u32 enable_mask;
	u32 register_bit;

@@ -148,9 +148,14 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
		return (AE_BAD_PARAMETER);
	}

	if (!(register_bit & gpe_register_info->mask_for_run)) {

		/* Write the updated enable mask */

	status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address);
		status =
		    acpi_hw_write(enable_mask,
				  &gpe_register_info->enable_address);
	}
	return (status);
}

@@ -242,6 +247,12 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
		local_event_status |= ACPI_EVENT_FLAG_ENABLED;
	}

	/* GPE currently masked? (masked for runtime?) */

	if (register_bit & gpe_register_info->mask_for_run) {
		local_event_status |= ACPI_EVENT_FLAG_MASKED;
	}

	/* GPE enabled for wake? */

	if (register_bit & gpe_register_info->enable_for_wake) {
@@ -397,6 +408,7 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
	u32 i;
	acpi_status status;
	struct acpi_gpe_register_info *gpe_register_info;
	u8 enable_mask;

	/* NOTE: assumes that all GPEs are currently disabled */

@@ -410,9 +422,10 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,

		/* Enable all "runtime" GPEs in this register */

		enable_mask = gpe_register_info->enable_for_run &
		    ~gpe_register_info->mask_for_run;
		status =
		    acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run,
					     gpe_register_info);
		    acpi_hw_gpe_enable_write(enable_mask, gpe_register_info);
		if (ACPI_FAILURE(status)) {
			return (status);
		}
Loading