diff options
Diffstat (limited to 'drivers/acpi/acpica/hwgpe.c')
| -rw-r--r-- | drivers/acpi/acpica/hwgpe.c | 340 |
1 files changed, 270 insertions, 70 deletions
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 96540506058f..386f4759c317 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -1,46 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 /****************************************************************************** * * Module Name: hwgpe - Low level GPE enable/disable/clear functions * + * Copyright (C) 2000 - 2025, Intel Corp. + * *****************************************************************************/ -/* - * Copyright (C) 2000 - 2013, Intel Corp. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * 3. Neither the names of the above-listed copyright holders nor the names - * of any contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - */ - #include <acpi/acpi.h> #include "accommon.h" #include "acevents.h" @@ -54,6 +20,80 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); +static acpi_status +acpi_hw_gpe_enable_write(u8 enable_mask, + struct acpi_gpe_register_info *gpe_register_info); + +/****************************************************************************** + * + * FUNCTION: acpi_hw_gpe_read + * + * PARAMETERS: value - Where the value is returned + * reg - GPE register structure + * + * RETURN: Status + * + * DESCRIPTION: Read from a GPE register in either memory or IO space. + * + * LIMITATIONS: <These limitations also apply to acpi_hw_gpe_write> + * space_ID must be system_memory or system_IO. + * + ******************************************************************************/ + +acpi_status acpi_hw_gpe_read(u64 *value, struct acpi_gpe_address *reg) +{ + acpi_status status; + u32 value32; + + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { +#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES + *value = (u64)ACPI_GET8((unsigned long)reg->address); + return_ACPI_STATUS(AE_OK); +#else + return acpi_os_read_memory((acpi_physical_address)reg->address, + value, ACPI_GPE_REGISTER_WIDTH); +#endif + } + + status = acpi_os_read_port((acpi_io_address)reg->address, + &value32, ACPI_GPE_REGISTER_WIDTH); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(status); + + *value = (u64)value32; + + return_ACPI_STATUS(AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_gpe_write + * + * PARAMETERS: value - Value to be written + * reg - GPE register structure + * + * RETURN: Status + * + * DESCRIPTION: Write to a GPE register in either memory or IO space. + * + ******************************************************************************/ + +acpi_status acpi_hw_gpe_write(u64 value, struct acpi_gpe_address *reg) +{ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { +#ifdef ACPI_GPE_USE_LOGICAL_ADDRESSES + ACPI_SET8((unsigned long)reg->address, value); + return_ACPI_STATUS(AE_OK); +#else + return acpi_os_write_memory((acpi_physical_address)reg->address, + value, ACPI_GPE_REGISTER_WIDTH); +#endif + } + + return acpi_os_write_port((acpi_io_address)reg->address, (u32)value, + ACPI_GPE_REGISTER_WIDTH); +} + /****************************************************************************** * * FUNCTION: acpi_hw_get_gpe_register_bit @@ -85,6 +125,8 @@ u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info) * RETURN: Status * * DESCRIPTION: Enable or disable a single GPE in the parent enable register. + * The enable_mask field of the involved GPE register must be + * updated by the caller if necessary. * ******************************************************************************/ @@ -92,8 +134,8 @@ 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; - u32 enable_mask; + acpi_status status = AE_OK; + u64 enable_mask; u32 register_bit; ACPI_FUNCTION_ENTRY(); @@ -107,7 +149,8 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) /* Get current value of the enable register that contains this GPE */ - status = acpi_hw_read(&enable_mask, &gpe_register_info->enable_address); + status = acpi_hw_gpe_read(&enable_mask, + &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -118,13 +161,13 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) switch (action) { case ACPI_GPE_CONDITIONAL_ENABLE: - /* Only enable if the enable_for_run bit is set */ + /* Only enable if the corresponding enable_mask bit is set */ - if (!(register_bit & gpe_register_info->enable_for_run)) { + if (!(register_bit & gpe_register_info->enable_mask)) { return (AE_BAD_PARAMETER); } - /*lint -fallthrough */ + ACPI_FALLTHROUGH; case ACPI_GPE_ENABLE: @@ -142,9 +185,13 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) return (AE_BAD_PARAMETER); } - /* Write the updated enable mask */ + 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_gpe_write(enable_mask, + &gpe_register_info->enable_address); + } return (status); } @@ -160,7 +207,7 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) * ******************************************************************************/ -acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) +acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info *gpe_event_info) { struct acpi_gpe_register_info *gpe_register_info; acpi_status status; @@ -181,9 +228,8 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) */ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); - status = acpi_hw_write(register_bit, - &gpe_register_info->status_address); - + status = acpi_hw_gpe_write(register_bit, + &gpe_register_info->status_address); return (status); } @@ -201,10 +247,10 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) ******************************************************************************/ acpi_status -acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, - acpi_event_status * event_status) +acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, + acpi_event_status *event_status) { - u32 in_byte; + u64 in_byte; u32 register_bit; struct acpi_gpe_register_info *gpe_register_info; acpi_event_status local_event_status = 0; @@ -216,6 +262,13 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, return (AE_BAD_PARAMETER); } + /* GPE currently handled? */ + + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_NONE) { + local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; + } + /* Get the info block for the entire GPE register */ gpe_register_info = gpe_event_info->register_info; @@ -230,21 +283,38 @@ 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) { local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED; } + /* GPE currently enabled (enable bit == 1)? */ + + status = acpi_hw_gpe_read(&in_byte, &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (register_bit & in_byte) { + local_event_status |= ACPI_EVENT_FLAG_ENABLE_SET; + } + /* GPE currently active (status bit == 1)? */ - status = acpi_hw_read(&in_byte, &gpe_register_info->status_address); + status = acpi_hw_gpe_read(&in_byte, &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { return (status); } if (register_bit & in_byte) { - local_event_status |= ACPI_EVENT_FLAG_SET; + local_event_status |= ACPI_EVENT_FLAG_STATUS_SET; } /* Set return value */ @@ -255,6 +325,32 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, /****************************************************************************** * + * FUNCTION: acpi_hw_gpe_enable_write + * + * PARAMETERS: enable_mask - Bit mask to write to the GPE register + * gpe_register_info - Gpe Register info + * + * RETURN: Status + * + * DESCRIPTION: Write the enable mask byte to the given GPE register. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_gpe_enable_write(u8 enable_mask, + struct acpi_gpe_register_info *gpe_register_info) +{ + acpi_status status; + + gpe_register_info->enable_mask = enable_mask; + + status = acpi_hw_gpe_write(enable_mask, + &gpe_register_info->enable_address); + return (status); +} + +/****************************************************************************** + * * FUNCTION: acpi_hw_disable_gpe_block * * PARAMETERS: gpe_xrupt_info - GPE Interrupt info @@ -280,8 +376,8 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Disable all GPEs in this register */ status = - acpi_hw_write(0x00, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(0x00, + &gpe_block->register_info[i]); if (ACPI_FAILURE(status)) { return (status); } @@ -316,9 +412,8 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Clear status on all GPEs in this register */ - status = - acpi_hw_write(0xFF, - &gpe_block->register_info[i].status_address); + status = acpi_hw_gpe_write(0xFF, + &gpe_block->register_info[i].status_address); if (ACPI_FAILURE(status)) { return (status); } @@ -343,26 +438,30 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, acpi_status acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info * gpe_block, + struct acpi_gpe_block_info *gpe_block, void *context) { u32 i; acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; + u8 enable_mask; /* NOTE: assumes that all GPEs are currently disabled */ /* Examine each GPE Register within the block */ for (i = 0; i < gpe_block->register_count; i++) { - if (!gpe_block->register_info[i].enable_for_run) { + gpe_register_info = &gpe_block->register_info[i]; + if (!gpe_register_info->enable_for_run) { continue; } /* Enable all "runtime" GPEs in this register */ + enable_mask = gpe_register_info->enable_for_run & + ~gpe_register_info->mask_for_run; status = - acpi_hw_write(gpe_block->register_info[i].enable_for_run, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(enable_mask, gpe_register_info); if (ACPI_FAILURE(status)) { return (status); } @@ -392,19 +491,21 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, { u32 i; acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; /* Examine each GPE Register within the block */ for (i = 0; i < gpe_block->register_count; i++) { - if (!gpe_block->register_info[i].enable_for_wake) { - continue; - } + gpe_register_info = &gpe_block->register_info[i]; - /* Enable all "wake" GPEs in this register */ + /* + * Enable all "wake" GPEs in this register and disable the + * remaining ones. + */ status = - acpi_hw_write(gpe_block->register_info[i].enable_for_wake, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(gpe_register_info->enable_for_wake, + gpe_register_info); if (ACPI_FAILURE(status)) { return (status); } @@ -413,6 +514,65 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, return (AE_OK); } +struct acpi_gpe_block_status_context { + struct acpi_gpe_register_info *gpe_skip_register_info; + u8 gpe_skip_mask; + u8 retval; +}; + +/****************************************************************************** + * + * FUNCTION: acpi_hw_get_gpe_block_status + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * context - GPE list walk context data + * + * 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 *context) +{ + struct acpi_gpe_block_status_context *c = context; + struct acpi_gpe_register_info *gpe_register_info; + u64 in_enable, in_status; + acpi_status status; + u8 ret_mask; + 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_gpe_read(&in_enable, + &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + continue; + } + + status = acpi_hw_gpe_read(&in_status, + &gpe_register_info->status_address); + if (ACPI_FAILURE(status)) { + continue; + } + + ret_mask = in_enable & in_status; + if (ret_mask && c->gpe_skip_register_info == gpe_register_info) { + ret_mask &= ~c->gpe_skip_mask; + } + c->retval |= ret_mask; + } + + return (AE_OK); +} + /****************************************************************************** * * FUNCTION: acpi_hw_disable_all_gpes @@ -432,7 +592,6 @@ acpi_status acpi_hw_disable_all_gpes(void) ACPI_FUNCTION_TRACE(hw_disable_all_gpes); status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); - status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); return_ACPI_STATUS(status); } @@ -480,4 +639,45 @@ acpi_status acpi_hw_enable_all_wakeup_gpes(void) return_ACPI_STATUS(status); } +/****************************************************************************** + * + * FUNCTION: acpi_hw_check_all_gpes + * + * PARAMETERS: gpe_skip_device - GPE devoce of the GPE to skip + * gpe_skip_number - Number of the GPE to skip + * + * RETURN: Combined status of all GPEs + * + * DESCRIPTION: Check all enabled GPEs in all GPE blocks, except for the one + * represented by the "skip" arguments, and return TRUE if the + * status bit is set for at least one of them of FALSE otherwise. + * + ******************************************************************************/ + +u8 acpi_hw_check_all_gpes(acpi_handle gpe_skip_device, u32 gpe_skip_number) +{ + struct acpi_gpe_block_status_context context = { + .gpe_skip_register_info = NULL, + .retval = 0, + }; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_hw_check_all_gpes); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_skip_device, + gpe_skip_number); + if (gpe_event_info) { + context.gpe_skip_register_info = gpe_event_info->register_info; + context.gpe_skip_mask = acpi_hw_get_gpe_register_bit(gpe_event_info); + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + (void)acpi_ev_walk_gpe_list(acpi_hw_get_gpe_block_status, &context); + return (context.retval != 0); +} + #endif /* !ACPI_REDUCED_HARDWARE */ |
