162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: hwregs - Read/write access functions for the various ACPI
562306a36Sopenharmony_ci *                       control and status registers.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci ******************************************************************************/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <acpi/acpi.h>
1062306a36Sopenharmony_ci#include "accommon.h"
1162306a36Sopenharmony_ci#include "acevents.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define _COMPONENT          ACPI_HARDWARE
1462306a36Sopenharmony_ciACPI_MODULE_NAME("hwregs")
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE)
1762306a36Sopenharmony_ci/* Local Prototypes */
1862306a36Sopenharmony_cistatic u8
1962306a36Sopenharmony_ciacpi_hw_get_access_bit_width(u64 address,
2062306a36Sopenharmony_ci			     struct acpi_generic_address *reg,
2162306a36Sopenharmony_ci			     u8 max_bit_width);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic acpi_status
2462306a36Sopenharmony_ciacpi_hw_read_multiple(u32 *value,
2562306a36Sopenharmony_ci		      struct acpi_generic_address *register_a,
2662306a36Sopenharmony_ci		      struct acpi_generic_address *register_b);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic acpi_status
2962306a36Sopenharmony_ciacpi_hw_write_multiple(u32 value,
3062306a36Sopenharmony_ci		       struct acpi_generic_address *register_a,
3162306a36Sopenharmony_ci		       struct acpi_generic_address *register_b);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#endif				/* !ACPI_REDUCED_HARDWARE */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/******************************************************************************
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * FUNCTION:    acpi_hw_get_access_bit_width
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * PARAMETERS:  address             - GAS register address
4062306a36Sopenharmony_ci *              reg                 - GAS register structure
4162306a36Sopenharmony_ci *              max_bit_width       - Max bit_width supported (32 or 64)
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * RETURN:      Status
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * DESCRIPTION: Obtain optimal access bit width
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci ******************************************************************************/
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic u8
5062306a36Sopenharmony_ciacpi_hw_get_access_bit_width(u64 address,
5162306a36Sopenharmony_ci			     struct acpi_generic_address *reg, u8 max_bit_width)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u8 access_bit_width;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/*
5662306a36Sopenharmony_ci	 * GAS format "register", used by FADT:
5762306a36Sopenharmony_ci	 *  1. Detected if bit_offset is 0 and bit_width is 8/16/32/64;
5862306a36Sopenharmony_ci	 *  2. access_size field is ignored and bit_width field is used for
5962306a36Sopenharmony_ci	 *     determining the boundary of the IO accesses.
6062306a36Sopenharmony_ci	 * GAS format "region", used by APEI registers:
6162306a36Sopenharmony_ci	 *  1. Detected if bit_offset is not 0 or bit_width is not 8/16/32/64;
6262306a36Sopenharmony_ci	 *  2. access_size field is used for determining the boundary of the
6362306a36Sopenharmony_ci	 *     IO accesses;
6462306a36Sopenharmony_ci	 *  3. bit_offset/bit_width fields are used to describe the "region".
6562306a36Sopenharmony_ci	 *
6662306a36Sopenharmony_ci	 * Note: This algorithm assumes that the "Address" fields should always
6762306a36Sopenharmony_ci	 *       contain aligned values.
6862306a36Sopenharmony_ci	 */
6962306a36Sopenharmony_ci	if (!reg->bit_offset && reg->bit_width &&
7062306a36Sopenharmony_ci	    ACPI_IS_POWER_OF_TWO(reg->bit_width) &&
7162306a36Sopenharmony_ci	    ACPI_IS_ALIGNED(reg->bit_width, 8)) {
7262306a36Sopenharmony_ci		access_bit_width = reg->bit_width;
7362306a36Sopenharmony_ci	} else if (reg->access_width) {
7462306a36Sopenharmony_ci		access_bit_width = ACPI_ACCESS_BIT_WIDTH(reg->access_width);
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		access_bit_width =
7762306a36Sopenharmony_ci		    ACPI_ROUND_UP_POWER_OF_TWO_8(reg->bit_offset +
7862306a36Sopenharmony_ci						 reg->bit_width);
7962306a36Sopenharmony_ci		if (access_bit_width <= 8) {
8062306a36Sopenharmony_ci			access_bit_width = 8;
8162306a36Sopenharmony_ci		} else {
8262306a36Sopenharmony_ci			while (!ACPI_IS_ALIGNED(address, access_bit_width >> 3)) {
8362306a36Sopenharmony_ci				access_bit_width >>= 1;
8462306a36Sopenharmony_ci			}
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Maximum IO port access bit width is 32 */
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
9162306a36Sopenharmony_ci		max_bit_width = 32;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/*
9562306a36Sopenharmony_ci	 * Return access width according to the requested maximum access bit width,
9662306a36Sopenharmony_ci	 * as the caller should know the format of the register and may enforce
9762306a36Sopenharmony_ci	 * a 32-bit accesses.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	if (access_bit_width < max_bit_width) {
10062306a36Sopenharmony_ci		return (access_bit_width);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	return (max_bit_width);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/******************************************************************************
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * FUNCTION:    acpi_hw_validate_register
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * PARAMETERS:  reg                 - GAS register structure
11062306a36Sopenharmony_ci *              max_bit_width       - Max bit_width supported (32 or 64)
11162306a36Sopenharmony_ci *              address             - Pointer to where the gas->address
11262306a36Sopenharmony_ci *                                    is returned
11362306a36Sopenharmony_ci *
11462306a36Sopenharmony_ci * RETURN:      Status
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS
11762306a36Sopenharmony_ci *              pointer, Address, space_id, bit_width, and bit_offset.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci ******************************************************************************/
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciacpi_status
12262306a36Sopenharmony_ciacpi_hw_validate_register(struct acpi_generic_address *reg,
12362306a36Sopenharmony_ci			  u8 max_bit_width, u64 *address)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u8 bit_width;
12662306a36Sopenharmony_ci	u8 access_width;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Must have a valid pointer to a GAS structure */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!reg) {
13162306a36Sopenharmony_ci		return (AE_BAD_PARAMETER);
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * Copy the target address. This handles possible alignment issues.
13662306a36Sopenharmony_ci	 * Address must not be null. A null address also indicates an optional
13762306a36Sopenharmony_ci	 * ACPI register that is not supported, so no error message.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	ACPI_MOVE_64_TO_64(address, &reg->address);
14062306a36Sopenharmony_ci	if (!(*address)) {
14162306a36Sopenharmony_ci		return (AE_BAD_ADDRESS);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Validate the space_ID */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if ((reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
14762306a36Sopenharmony_ci	    (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
14862306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
14962306a36Sopenharmony_ci			    "Unsupported address space: 0x%X", reg->space_id));
15062306a36Sopenharmony_ci		return (AE_SUPPORT);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Validate the access_width */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (reg->access_width > 4) {
15662306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
15762306a36Sopenharmony_ci			    "Unsupported register access width: 0x%X",
15862306a36Sopenharmony_ci			    reg->access_width));
15962306a36Sopenharmony_ci		return (AE_SUPPORT);
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* Validate the bit_width, convert access_width into number of bits */
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	access_width =
16562306a36Sopenharmony_ci	    acpi_hw_get_access_bit_width(*address, reg, max_bit_width);
16662306a36Sopenharmony_ci	bit_width =
16762306a36Sopenharmony_ci	    ACPI_ROUND_UP(reg->bit_offset + reg->bit_width, access_width);
16862306a36Sopenharmony_ci	if (max_bit_width < bit_width) {
16962306a36Sopenharmony_ci		ACPI_WARNING((AE_INFO,
17062306a36Sopenharmony_ci			      "Requested bit width 0x%X is smaller than register bit width 0x%X",
17162306a36Sopenharmony_ci			      max_bit_width, bit_width));
17262306a36Sopenharmony_ci		return (AE_SUPPORT);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return (AE_OK);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/******************************************************************************
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci * FUNCTION:    acpi_hw_read
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * PARAMETERS:  value               - Where the value is returned
18362306a36Sopenharmony_ci *              reg                 - GAS register structure
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * RETURN:      Status
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * DESCRIPTION: Read from either memory or IO space. This is a 64-bit max
18862306a36Sopenharmony_ci *              version of acpi_read.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * LIMITATIONS: <These limitations also apply to acpi_hw_write>
19162306a36Sopenharmony_ci *      space_ID must be system_memory or system_IO.
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci ******************************************************************************/
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciacpi_status acpi_hw_read(u64 *value, struct acpi_generic_address *reg)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	u64 address;
19862306a36Sopenharmony_ci	u8 access_width;
19962306a36Sopenharmony_ci	u32 bit_width;
20062306a36Sopenharmony_ci	u8 bit_offset;
20162306a36Sopenharmony_ci	u64 value64;
20262306a36Sopenharmony_ci	u32 value32;
20362306a36Sopenharmony_ci	u8 index;
20462306a36Sopenharmony_ci	acpi_status status;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ACPI_FUNCTION_NAME(hw_read);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Validate contents of the GAS register */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	status = acpi_hw_validate_register(reg, 64, &address);
21162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
21262306a36Sopenharmony_ci		return (status);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * Initialize entire 64-bit return value to zero, convert access_width
21762306a36Sopenharmony_ci	 * into number of bits based
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	*value = 0;
22062306a36Sopenharmony_ci	access_width = acpi_hw_get_access_bit_width(address, reg, 64);
22162306a36Sopenharmony_ci	bit_width = reg->bit_offset + reg->bit_width;
22262306a36Sopenharmony_ci	bit_offset = reg->bit_offset;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/*
22562306a36Sopenharmony_ci	 * Two address spaces supported: Memory or IO. PCI_Config is
22662306a36Sopenharmony_ci	 * not supported here because the GAS structure is insufficient
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	index = 0;
22962306a36Sopenharmony_ci	while (bit_width) {
23062306a36Sopenharmony_ci		if (bit_offset >= access_width) {
23162306a36Sopenharmony_ci			value64 = 0;
23262306a36Sopenharmony_ci			bit_offset -= access_width;
23362306a36Sopenharmony_ci		} else {
23462306a36Sopenharmony_ci			if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
23562306a36Sopenharmony_ci				status =
23662306a36Sopenharmony_ci				    acpi_os_read_memory((acpi_physical_address)
23762306a36Sopenharmony_ci							address +
23862306a36Sopenharmony_ci							index *
23962306a36Sopenharmony_ci							ACPI_DIV_8
24062306a36Sopenharmony_ci							(access_width),
24162306a36Sopenharmony_ci							&value64, access_width);
24262306a36Sopenharmony_ci			} else {	/* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci				status = acpi_hw_read_port((acpi_io_address)
24562306a36Sopenharmony_ci							   address +
24662306a36Sopenharmony_ci							   index *
24762306a36Sopenharmony_ci							   ACPI_DIV_8
24862306a36Sopenharmony_ci							   (access_width),
24962306a36Sopenharmony_ci							   &value32,
25062306a36Sopenharmony_ci							   access_width);
25162306a36Sopenharmony_ci				value64 = (u64)value32;
25262306a36Sopenharmony_ci			}
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/*
25662306a36Sopenharmony_ci		 * Use offset style bit writes because "Index * AccessWidth" is
25762306a36Sopenharmony_ci		 * ensured to be less than 64-bits by acpi_hw_validate_register().
25862306a36Sopenharmony_ci		 */
25962306a36Sopenharmony_ci		ACPI_SET_BITS(value, index * access_width,
26062306a36Sopenharmony_ci			      ACPI_MASK_BITS_ABOVE_64(access_width), value64);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		bit_width -=
26362306a36Sopenharmony_ci		    bit_width > access_width ? access_width : bit_width;
26462306a36Sopenharmony_ci		index++;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_IO,
26862306a36Sopenharmony_ci			  "Read:  %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n",
26962306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(*value), access_width,
27062306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(address),
27162306a36Sopenharmony_ci			  acpi_ut_get_region_name(reg->space_id)));
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return (status);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/******************************************************************************
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * FUNCTION:    acpi_hw_write
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * PARAMETERS:  value               - Value to be written
28162306a36Sopenharmony_ci *              reg                 - GAS register structure
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * RETURN:      Status
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci * DESCRIPTION: Write to either memory or IO space. This is a 64-bit max
28662306a36Sopenharmony_ci *              version of acpi_write.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci ******************************************************************************/
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ciacpi_status acpi_hw_write(u64 value, struct acpi_generic_address *reg)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	u64 address;
29362306a36Sopenharmony_ci	u8 access_width;
29462306a36Sopenharmony_ci	u32 bit_width;
29562306a36Sopenharmony_ci	u8 bit_offset;
29662306a36Sopenharmony_ci	u64 value64;
29762306a36Sopenharmony_ci	u8 index;
29862306a36Sopenharmony_ci	acpi_status status;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	ACPI_FUNCTION_NAME(hw_write);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Validate contents of the GAS register */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	status = acpi_hw_validate_register(reg, 64, &address);
30562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
30662306a36Sopenharmony_ci		return (status);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* Convert access_width into number of bits based */
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	access_width = acpi_hw_get_access_bit_width(address, reg, 64);
31262306a36Sopenharmony_ci	bit_width = reg->bit_offset + reg->bit_width;
31362306a36Sopenharmony_ci	bit_offset = reg->bit_offset;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * Two address spaces supported: Memory or IO. PCI_Config is
31762306a36Sopenharmony_ci	 * not supported here because the GAS structure is insufficient
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	index = 0;
32062306a36Sopenharmony_ci	while (bit_width) {
32162306a36Sopenharmony_ci		/*
32262306a36Sopenharmony_ci		 * Use offset style bit reads because "Index * AccessWidth" is
32362306a36Sopenharmony_ci		 * ensured to be less than 64-bits by acpi_hw_validate_register().
32462306a36Sopenharmony_ci		 */
32562306a36Sopenharmony_ci		value64 = ACPI_GET_BITS(&value, index * access_width,
32662306a36Sopenharmony_ci					ACPI_MASK_BITS_ABOVE_64(access_width));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (bit_offset >= access_width) {
32962306a36Sopenharmony_ci			bit_offset -= access_width;
33062306a36Sopenharmony_ci		} else {
33162306a36Sopenharmony_ci			if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
33262306a36Sopenharmony_ci				status =
33362306a36Sopenharmony_ci				    acpi_os_write_memory((acpi_physical_address)
33462306a36Sopenharmony_ci							 address +
33562306a36Sopenharmony_ci							 index *
33662306a36Sopenharmony_ci							 ACPI_DIV_8
33762306a36Sopenharmony_ci							 (access_width),
33862306a36Sopenharmony_ci							 value64, access_width);
33962306a36Sopenharmony_ci			} else {	/* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci				status = acpi_hw_write_port((acpi_io_address)
34262306a36Sopenharmony_ci							    address +
34362306a36Sopenharmony_ci							    index *
34462306a36Sopenharmony_ci							    ACPI_DIV_8
34562306a36Sopenharmony_ci							    (access_width),
34662306a36Sopenharmony_ci							    (u32)value64,
34762306a36Sopenharmony_ci							    access_width);
34862306a36Sopenharmony_ci			}
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		/*
35262306a36Sopenharmony_ci		 * Index * access_width is ensured to be less than 32-bits by
35362306a36Sopenharmony_ci		 * acpi_hw_validate_register().
35462306a36Sopenharmony_ci		 */
35562306a36Sopenharmony_ci		bit_width -=
35662306a36Sopenharmony_ci		    bit_width > access_width ? access_width : bit_width;
35762306a36Sopenharmony_ci		index++;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_IO,
36162306a36Sopenharmony_ci			  "Wrote: %8.8X%8.8X width %2d   to %8.8X%8.8X (%s)\n",
36262306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(value), access_width,
36362306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(address),
36462306a36Sopenharmony_ci			  acpi_ut_get_region_name(reg->space_id)));
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return (status);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE)
37062306a36Sopenharmony_ci/*******************************************************************************
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * FUNCTION:    acpi_hw_clear_acpi_status
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * PARAMETERS:  None
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * RETURN:      Status
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * DESCRIPTION: Clears all fixed and general purpose status bits
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci ******************************************************************************/
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciacpi_status acpi_hw_clear_acpi_status(void)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	acpi_status status;
38562306a36Sopenharmony_ci	acpi_cpu_flags lock_flags = 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_clear_acpi_status);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
39062306a36Sopenharmony_ci			  ACPI_BITMASK_ALL_FIXED_STATUS,
39162306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address)));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* Clear the fixed events in PM1 A/B */
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
39862306a36Sopenharmony_ci					ACPI_BITMASK_ALL_FIXED_STATUS);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
40362306a36Sopenharmony_ci		goto exit;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Clear the GPE Bits in all GPE registers in all GPE blocks */
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ciexit:
41162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/*******************************************************************************
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * FUNCTION:    acpi_hw_get_bit_register_info
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * PARAMETERS:  register_id         - Index of ACPI Register to access
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * RETURN:      The bitmask to be used when accessing the register
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * DESCRIPTION: Map register_id into a register bitmask.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci ******************************************************************************/
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistruct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	ACPI_FUNCTION_ENTRY();
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (register_id > ACPI_BITREG_MAX) {
43162306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: 0x%X",
43262306a36Sopenharmony_ci			    register_id));
43362306a36Sopenharmony_ci		return (NULL);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return (&acpi_gbl_bit_register_info[register_id]);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/******************************************************************************
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * FUNCTION:    acpi_hw_write_pm1_control
44262306a36Sopenharmony_ci *
44362306a36Sopenharmony_ci * PARAMETERS:  pm1a_control        - Value to be written to PM1A control
44462306a36Sopenharmony_ci *              pm1b_control        - Value to be written to PM1B control
44562306a36Sopenharmony_ci *
44662306a36Sopenharmony_ci * RETURN:      Status
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * DESCRIPTION: Write the PM1 A/B control registers. These registers are
44962306a36Sopenharmony_ci *              different than the PM1 A/B status and enable registers
45062306a36Sopenharmony_ci *              in that different values can be written to the A/B registers.
45162306a36Sopenharmony_ci *              Most notably, the SLP_TYP bits can be different, as per the
45262306a36Sopenharmony_ci *              values returned from the _Sx predefined methods.
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci ******************************************************************************/
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ciacpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	acpi_status status;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_write_pm1_control);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	status =
46362306a36Sopenharmony_ci	    acpi_hw_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block);
46462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
46562306a36Sopenharmony_ci		return_ACPI_STATUS(status);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (acpi_gbl_FADT.xpm1b_control_block.address) {
46962306a36Sopenharmony_ci		status =
47062306a36Sopenharmony_ci		    acpi_hw_write(pm1b_control,
47162306a36Sopenharmony_ci				  &acpi_gbl_FADT.xpm1b_control_block);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	return_ACPI_STATUS(status);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/******************************************************************************
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * FUNCTION:    acpi_hw_register_read
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * PARAMETERS:  register_id         - ACPI Register ID
48162306a36Sopenharmony_ci *              return_value        - Where the register value is returned
48262306a36Sopenharmony_ci *
48362306a36Sopenharmony_ci * RETURN:      Status and the value read.
48462306a36Sopenharmony_ci *
48562306a36Sopenharmony_ci * DESCRIPTION: Read from the specified ACPI register
48662306a36Sopenharmony_ci *
48762306a36Sopenharmony_ci ******************************************************************************/
48862306a36Sopenharmony_ciacpi_status acpi_hw_register_read(u32 register_id, u32 *return_value)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	u32 value = 0;
49162306a36Sopenharmony_ci	u64 value64;
49262306a36Sopenharmony_ci	acpi_status status;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_register_read);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	switch (register_id) {
49762306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		status = acpi_hw_read_multiple(&value,
50062306a36Sopenharmony_ci					       &acpi_gbl_xpm1a_status,
50162306a36Sopenharmony_ci					       &acpi_gbl_xpm1b_status);
50262306a36Sopenharmony_ci		break;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access each */
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		status = acpi_hw_read_multiple(&value,
50762306a36Sopenharmony_ci					       &acpi_gbl_xpm1a_enable,
50862306a36Sopenharmony_ci					       &acpi_gbl_xpm1b_enable);
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		status = acpi_hw_read_multiple(&value,
51462306a36Sopenharmony_ci					       &acpi_gbl_FADT.
51562306a36Sopenharmony_ci					       xpm1a_control_block,
51662306a36Sopenharmony_ci					       &acpi_gbl_FADT.
51762306a36Sopenharmony_ci					       xpm1b_control_block);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/*
52062306a36Sopenharmony_ci		 * Zero the write-only bits. From the ACPI specification, "Hardware
52162306a36Sopenharmony_ci		 * Write-Only Bits": "Upon reads to registers with write-only bits,
52262306a36Sopenharmony_ci		 * software masks out all write-only bits."
52362306a36Sopenharmony_ci		 */
52462306a36Sopenharmony_ci		value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		status =
53062306a36Sopenharmony_ci		    acpi_hw_read(&value64, &acpi_gbl_FADT.xpm2_control_block);
53162306a36Sopenharmony_ci		if (ACPI_SUCCESS(status)) {
53262306a36Sopenharmony_ci			value = (u32)value64;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		status = acpi_hw_read(&value64, &acpi_gbl_FADT.xpm_timer_block);
53962306a36Sopenharmony_ci		if (ACPI_SUCCESS(status)) {
54062306a36Sopenharmony_ci			value = (u32)value64;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		status =
54862306a36Sopenharmony_ci		    acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8);
54962306a36Sopenharmony_ci		break;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	default:
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
55462306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
55962306a36Sopenharmony_ci		*return_value = (u32)value;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return_ACPI_STATUS(status);
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/******************************************************************************
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * FUNCTION:    acpi_hw_register_write
56862306a36Sopenharmony_ci *
56962306a36Sopenharmony_ci * PARAMETERS:  register_id         - ACPI Register ID
57062306a36Sopenharmony_ci *              value               - The value to write
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * RETURN:      Status
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci * DESCRIPTION: Write to the specified ACPI register
57562306a36Sopenharmony_ci *
57662306a36Sopenharmony_ci * NOTE: In accordance with the ACPI specification, this function automatically
57762306a36Sopenharmony_ci * preserves the value of the following bits, meaning that these bits cannot be
57862306a36Sopenharmony_ci * changed via this interface:
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci * PM1_CONTROL[0] = SCI_EN
58162306a36Sopenharmony_ci * PM1_CONTROL[9]
58262306a36Sopenharmony_ci * PM1_STATUS[11]
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * ACPI References:
58562306a36Sopenharmony_ci * 1) Hardware Ignored Bits: When software writes to a register with ignored
58662306a36Sopenharmony_ci *      bit fields, it preserves the ignored bit fields
58762306a36Sopenharmony_ci * 2) SCI_EN: OSPM always preserves this bit position
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci ******************************************************************************/
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciacpi_status acpi_hw_register_write(u32 register_id, u32 value)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	acpi_status status;
59462306a36Sopenharmony_ci	u32 read_value;
59562306a36Sopenharmony_ci	u64 read_value64;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(hw_register_write);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	switch (register_id) {
60062306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
60162306a36Sopenharmony_ci		/*
60262306a36Sopenharmony_ci		 * Handle the "ignored" bit in PM1 Status. According to the ACPI
60362306a36Sopenharmony_ci		 * specification, ignored bits are to be preserved when writing.
60462306a36Sopenharmony_ci		 * Normally, this would mean a read/modify/write sequence. However,
60562306a36Sopenharmony_ci		 * preserving a bit in the status register is different. Writing a
60662306a36Sopenharmony_ci		 * one clears the status, and writing a zero preserves the status.
60762306a36Sopenharmony_ci		 * Therefore, we must always write zero to the ignored bit.
60862306a36Sopenharmony_ci		 *
60962306a36Sopenharmony_ci		 * This behavior is clarified in the ACPI 4.0 specification.
61062306a36Sopenharmony_ci		 */
61162306a36Sopenharmony_ci		value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		status = acpi_hw_write_multiple(value,
61462306a36Sopenharmony_ci						&acpi_gbl_xpm1a_status,
61562306a36Sopenharmony_ci						&acpi_gbl_xpm1b_status);
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access each */
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		status = acpi_hw_write_multiple(value,
62162306a36Sopenharmony_ci						&acpi_gbl_xpm1a_enable,
62262306a36Sopenharmony_ci						&acpi_gbl_xpm1b_enable);
62362306a36Sopenharmony_ci		break;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
62662306a36Sopenharmony_ci		/*
62762306a36Sopenharmony_ci		 * Perform a read first to preserve certain bits (per ACPI spec)
62862306a36Sopenharmony_ci		 * Note: This includes SCI_EN, we never want to change this bit
62962306a36Sopenharmony_ci		 */
63062306a36Sopenharmony_ci		status = acpi_hw_read_multiple(&read_value,
63162306a36Sopenharmony_ci					       &acpi_gbl_FADT.
63262306a36Sopenharmony_ci					       xpm1a_control_block,
63362306a36Sopenharmony_ci					       &acpi_gbl_FADT.
63462306a36Sopenharmony_ci					       xpm1b_control_block);
63562306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
63662306a36Sopenharmony_ci			goto exit;
63762306a36Sopenharmony_ci		}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		/* Insert the bits to be preserved */
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS,
64262306a36Sopenharmony_ci				 read_value);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		/* Now we can write the data */
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		status = acpi_hw_write_multiple(value,
64762306a36Sopenharmony_ci						&acpi_gbl_FADT.
64862306a36Sopenharmony_ci						xpm1a_control_block,
64962306a36Sopenharmony_ci						&acpi_gbl_FADT.
65062306a36Sopenharmony_ci						xpm1b_control_block);
65162306a36Sopenharmony_ci		break;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
65462306a36Sopenharmony_ci		/*
65562306a36Sopenharmony_ci		 * For control registers, all reserved bits must be preserved,
65662306a36Sopenharmony_ci		 * as per the ACPI spec.
65762306a36Sopenharmony_ci		 */
65862306a36Sopenharmony_ci		status =
65962306a36Sopenharmony_ci		    acpi_hw_read(&read_value64,
66062306a36Sopenharmony_ci				 &acpi_gbl_FADT.xpm2_control_block);
66162306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
66262306a36Sopenharmony_ci			goto exit;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci		read_value = (u32)read_value64;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		/* Insert the bits to be preserved */
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS,
66962306a36Sopenharmony_ci				 read_value);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		status =
67262306a36Sopenharmony_ci		    acpi_hw_write(value, &acpi_gbl_FADT.xpm2_control_block);
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		status = acpi_hw_write(value, &acpi_gbl_FADT.xpm_timer_block);
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		/* SMI_CMD is currently always in IO space */
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		status =
68562306a36Sopenharmony_ci		    acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8);
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	default:
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
69162306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ciexit:
69662306a36Sopenharmony_ci	return_ACPI_STATUS(status);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/******************************************************************************
70062306a36Sopenharmony_ci *
70162306a36Sopenharmony_ci * FUNCTION:    acpi_hw_read_multiple
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * PARAMETERS:  value               - Where the register value is returned
70462306a36Sopenharmony_ci *              register_a           - First ACPI register (required)
70562306a36Sopenharmony_ci *              register_b           - Second ACPI register (optional)
70662306a36Sopenharmony_ci *
70762306a36Sopenharmony_ci * RETURN:      Status
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
71062306a36Sopenharmony_ci *
71162306a36Sopenharmony_ci ******************************************************************************/
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic acpi_status
71462306a36Sopenharmony_ciacpi_hw_read_multiple(u32 *value,
71562306a36Sopenharmony_ci		      struct acpi_generic_address *register_a,
71662306a36Sopenharmony_ci		      struct acpi_generic_address *register_b)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	u32 value_a = 0;
71962306a36Sopenharmony_ci	u32 value_b = 0;
72062306a36Sopenharmony_ci	u64 value64;
72162306a36Sopenharmony_ci	acpi_status status;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* The first register is always required */
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	status = acpi_hw_read(&value64, register_a);
72662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
72762306a36Sopenharmony_ci		return (status);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	value_a = (u32)value64;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* Second register is optional */
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (register_b->address) {
73462306a36Sopenharmony_ci		status = acpi_hw_read(&value64, register_b);
73562306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
73662306a36Sopenharmony_ci			return (status);
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci		value_b = (u32)value64;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/*
74262306a36Sopenharmony_ci	 * OR the two return values together. No shifting or masking is necessary,
74362306a36Sopenharmony_ci	 * because of how the PM1 registers are defined in the ACPI specification:
74462306a36Sopenharmony_ci	 *
74562306a36Sopenharmony_ci	 * "Although the bits can be split between the two register blocks (each
74662306a36Sopenharmony_ci	 * register block has a unique pointer within the FADT), the bit positions
74762306a36Sopenharmony_ci	 * are maintained. The register block with unimplemented bits (that is,
74862306a36Sopenharmony_ci	 * those implemented in the other register block) always returns zeros,
74962306a36Sopenharmony_ci	 * and writes have no side effects"
75062306a36Sopenharmony_ci	 */
75162306a36Sopenharmony_ci	*value = (value_a | value_b);
75262306a36Sopenharmony_ci	return (AE_OK);
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci/******************************************************************************
75662306a36Sopenharmony_ci *
75762306a36Sopenharmony_ci * FUNCTION:    acpi_hw_write_multiple
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * PARAMETERS:  value               - The value to write
76062306a36Sopenharmony_ci *              register_a           - First ACPI register (required)
76162306a36Sopenharmony_ci *              register_b           - Second ACPI register (optional)
76262306a36Sopenharmony_ci *
76362306a36Sopenharmony_ci * RETURN:      Status
76462306a36Sopenharmony_ci *
76562306a36Sopenharmony_ci * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
76662306a36Sopenharmony_ci *
76762306a36Sopenharmony_ci ******************************************************************************/
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic acpi_status
77062306a36Sopenharmony_ciacpi_hw_write_multiple(u32 value,
77162306a36Sopenharmony_ci		       struct acpi_generic_address *register_a,
77262306a36Sopenharmony_ci		       struct acpi_generic_address *register_b)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	acpi_status status;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* The first register is always required */
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	status = acpi_hw_write(value, register_a);
77962306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
78062306a36Sopenharmony_ci		return (status);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/*
78462306a36Sopenharmony_ci	 * Second register is optional
78562306a36Sopenharmony_ci	 *
78662306a36Sopenharmony_ci	 * No bit shifting or clearing is necessary, because of how the PM1
78762306a36Sopenharmony_ci	 * registers are defined in the ACPI specification:
78862306a36Sopenharmony_ci	 *
78962306a36Sopenharmony_ci	 * "Although the bits can be split between the two register blocks (each
79062306a36Sopenharmony_ci	 * register block has a unique pointer within the FADT), the bit positions
79162306a36Sopenharmony_ci	 * are maintained. The register block with unimplemented bits (that is,
79262306a36Sopenharmony_ci	 * those implemented in the other register block) always returns zeros,
79362306a36Sopenharmony_ci	 * and writes have no side effects"
79462306a36Sopenharmony_ci	 */
79562306a36Sopenharmony_ci	if (register_b->address) {
79662306a36Sopenharmony_ci		status = acpi_hw_write(value, register_b);
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return (status);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci#endif				/* !ACPI_REDUCED_HARDWARE */
803