xref: /openbmc/linux/drivers/acpi/acpica/hwregs.c (revision 1cf0cee1)
195857638SErik Schmauss // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
295b482a8SLen Brown /*******************************************************************************
395b482a8SLen Brown  *
495b482a8SLen Brown  * Module Name: hwregs - Read/write access functions for the various ACPI
595b482a8SLen Brown  *                       control and status registers.
695b482a8SLen Brown  *
795b482a8SLen Brown  ******************************************************************************/
895b482a8SLen Brown 
995b482a8SLen Brown #include <acpi/acpi.h>
10e2f7a777SLen Brown #include "accommon.h"
11e2f7a777SLen Brown #include "acevents.h"
1295b482a8SLen Brown 
1395b482a8SLen Brown #define _COMPONENT          ACPI_HARDWARE
1495b482a8SLen Brown ACPI_MODULE_NAME("hwregs")
1595b482a8SLen Brown 
1633620c54SBob Moore #if (!ACPI_REDUCED_HARDWARE)
17c520abadSBob Moore /* Local Prototypes */
18b314a172SLv Zheng static u8
1904cf0537SLv Zheng acpi_hw_get_access_bit_width(u64 address,
2004cf0537SLv Zheng 			     struct acpi_generic_address *reg,
21b314a172SLv Zheng 			     u8 max_bit_width);
22b314a172SLv Zheng 
23c520abadSBob Moore static acpi_status
24c520abadSBob Moore acpi_hw_read_multiple(u32 *value,
25c520abadSBob Moore 		      struct acpi_generic_address *register_a,
26c520abadSBob Moore 		      struct acpi_generic_address *register_b);
27c520abadSBob Moore 
28c520abadSBob Moore static acpi_status
29c520abadSBob Moore acpi_hw_write_multiple(u32 value,
30c520abadSBob Moore 		       struct acpi_generic_address *register_a,
31c520abadSBob Moore 		       struct acpi_generic_address *register_b);
32c520abadSBob Moore 
3333620c54SBob Moore #endif				/* !ACPI_REDUCED_HARDWARE */
3433620c54SBob Moore 
35c6b5774cSBob Moore /******************************************************************************
36c6b5774cSBob Moore  *
37b314a172SLv Zheng  * FUNCTION:    acpi_hw_get_access_bit_width
38b314a172SLv Zheng  *
3904cf0537SLv Zheng  * PARAMETERS:  address             - GAS register address
4004cf0537SLv Zheng  *              reg                 - GAS register structure
41b314a172SLv Zheng  *              max_bit_width       - Max bit_width supported (32 or 64)
42b314a172SLv Zheng  *
43b314a172SLv Zheng  * RETURN:      Status
44b314a172SLv Zheng  *
45b314a172SLv Zheng  * DESCRIPTION: Obtain optimal access bit width
46b314a172SLv Zheng  *
47b314a172SLv Zheng  ******************************************************************************/
48b314a172SLv Zheng 
49b314a172SLv Zheng static u8
acpi_hw_get_access_bit_width(u64 address,struct acpi_generic_address * reg,u8 max_bit_width)5004cf0537SLv Zheng acpi_hw_get_access_bit_width(u64 address,
5104cf0537SLv Zheng 			     struct acpi_generic_address *reg, u8 max_bit_width)
52b314a172SLv Zheng {
5304cf0537SLv Zheng 	u8 access_bit_width;
5404cf0537SLv Zheng 
5504cf0537SLv Zheng 	/*
5604cf0537SLv Zheng 	 * GAS format "register", used by FADT:
5704cf0537SLv Zheng 	 *  1. Detected if bit_offset is 0 and bit_width is 8/16/32/64;
5804cf0537SLv Zheng 	 *  2. access_size field is ignored and bit_width field is used for
5904cf0537SLv Zheng 	 *     determining the boundary of the IO accesses.
6004cf0537SLv Zheng 	 * GAS format "region", used by APEI registers:
6104cf0537SLv Zheng 	 *  1. Detected if bit_offset is not 0 or bit_width is not 8/16/32/64;
6204cf0537SLv Zheng 	 *  2. access_size field is used for determining the boundary of the
6304cf0537SLv Zheng 	 *     IO accesses;
6404cf0537SLv Zheng 	 *  3. bit_offset/bit_width fields are used to describe the "region".
6504cf0537SLv Zheng 	 *
6604cf0537SLv Zheng 	 * Note: This algorithm assumes that the "Address" fields should always
6704cf0537SLv Zheng 	 *       contain aligned values.
6804cf0537SLv Zheng 	 */
6904cf0537SLv Zheng 	if (!reg->bit_offset && reg->bit_width &&
7004cf0537SLv Zheng 	    ACPI_IS_POWER_OF_TWO(reg->bit_width) &&
7104cf0537SLv Zheng 	    ACPI_IS_ALIGNED(reg->bit_width, 8)) {
7204cf0537SLv Zheng 		access_bit_width = reg->bit_width;
7304cf0537SLv Zheng 	} else if (reg->access_width) {
744eebedd8SLv Zheng 		access_bit_width = ACPI_ACCESS_BIT_WIDTH(reg->access_width);
7504cf0537SLv Zheng 	} else {
7604cf0537SLv Zheng 		access_bit_width =
7704cf0537SLv Zheng 		    ACPI_ROUND_UP_POWER_OF_TWO_8(reg->bit_offset +
7804cf0537SLv Zheng 						 reg->bit_width);
7904cf0537SLv Zheng 		if (access_bit_width <= 8) {
8004cf0537SLv Zheng 			access_bit_width = 8;
8104cf0537SLv Zheng 		} else {
8204cf0537SLv Zheng 			while (!ACPI_IS_ALIGNED(address, access_bit_width >> 3)) {
8304cf0537SLv Zheng 				access_bit_width >>= 1;
8404cf0537SLv Zheng 			}
8504cf0537SLv Zheng 		}
8604cf0537SLv Zheng 	}
8704cf0537SLv Zheng 
8804cf0537SLv Zheng 	/* Maximum IO port access bit width is 32 */
8904cf0537SLv Zheng 
907f9bef9dSLv Zheng 	if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
917f9bef9dSLv Zheng 		max_bit_width = 32;
927f9bef9dSLv Zheng 	}
937f9bef9dSLv Zheng 
94b314a172SLv Zheng 	/*
9504cf0537SLv Zheng 	 * Return access width according to the requested maximum access bit width,
9604cf0537SLv Zheng 	 * as the caller should know the format of the register and may enforce
9704cf0537SLv Zheng 	 * a 32-bit accesses.
98b314a172SLv Zheng 	 */
9904cf0537SLv Zheng 	if (access_bit_width < max_bit_width) {
10004cf0537SLv Zheng 		return (access_bit_width);
1017f9bef9dSLv Zheng 	}
102b314a172SLv Zheng 	return (max_bit_width);
103b314a172SLv Zheng }
104b314a172SLv Zheng 
105b314a172SLv Zheng /******************************************************************************
106b314a172SLv Zheng  *
107c6b5774cSBob Moore  * FUNCTION:    acpi_hw_validate_register
108c6b5774cSBob Moore  *
109ba494beeSBob Moore  * PARAMETERS:  reg                 - GAS register structure
110c6b5774cSBob Moore  *              max_bit_width       - Max bit_width supported (32 or 64)
111ba494beeSBob Moore  *              address             - Pointer to where the gas->address
112c6b5774cSBob Moore  *                                    is returned
113c6b5774cSBob Moore  *
114c6b5774cSBob Moore  * RETURN:      Status
115c6b5774cSBob Moore  *
116c6b5774cSBob Moore  * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS
117c6b5774cSBob Moore  *              pointer, Address, space_id, bit_width, and bit_offset.
118c6b5774cSBob Moore  *
119c6b5774cSBob Moore  ******************************************************************************/
120c6b5774cSBob Moore 
121c6b5774cSBob Moore acpi_status
acpi_hw_validate_register(struct acpi_generic_address * reg,u8 max_bit_width,u64 * address)122c6b5774cSBob Moore acpi_hw_validate_register(struct acpi_generic_address *reg,
123c6b5774cSBob Moore 			  u8 max_bit_width, u64 *address)
124c6b5774cSBob Moore {
125920de6ebSLv Zheng 	u8 bit_width;
126920de6ebSLv Zheng 	u8 access_width;
127c6b5774cSBob Moore 
128c6b5774cSBob Moore 	/* Must have a valid pointer to a GAS structure */
129c6b5774cSBob Moore 
130c6b5774cSBob Moore 	if (!reg) {
131c6b5774cSBob Moore 		return (AE_BAD_PARAMETER);
132c6b5774cSBob Moore 	}
133c6b5774cSBob Moore 
134c6b5774cSBob Moore 	/*
135c6b5774cSBob Moore 	 * Copy the target address. This handles possible alignment issues.
136c6b5774cSBob Moore 	 * Address must not be null. A null address also indicates an optional
137c6b5774cSBob Moore 	 * ACPI register that is not supported, so no error message.
138c6b5774cSBob Moore 	 */
139c6b5774cSBob Moore 	ACPI_MOVE_64_TO_64(address, &reg->address);
140c6b5774cSBob Moore 	if (!(*address)) {
141c6b5774cSBob Moore 		return (AE_BAD_ADDRESS);
142c6b5774cSBob Moore 	}
143c6b5774cSBob Moore 
144ba494beeSBob Moore 	/* Validate the space_ID */
145c6b5774cSBob Moore 
146c6b5774cSBob Moore 	if ((reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
147c6b5774cSBob Moore 	    (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
148c6b5774cSBob Moore 		ACPI_ERROR((AE_INFO,
149c6b5774cSBob Moore 			    "Unsupported address space: 0x%X", reg->space_id));
150c6b5774cSBob Moore 		return (AE_SUPPORT);
151c6b5774cSBob Moore 	}
152c6b5774cSBob Moore 
153920de6ebSLv Zheng 	/* Validate the access_width */
154c6b5774cSBob Moore 
155920de6ebSLv Zheng 	if (reg->access_width > 4) {
156c6b5774cSBob Moore 		ACPI_ERROR((AE_INFO,
157920de6ebSLv Zheng 			    "Unsupported register access width: 0x%X",
158920de6ebSLv Zheng 			    reg->access_width));
159c6b5774cSBob Moore 		return (AE_SUPPORT);
160c6b5774cSBob Moore 	}
161c6b5774cSBob Moore 
162920de6ebSLv Zheng 	/* Validate the bit_width, convert access_width into number of bits */
163c6b5774cSBob Moore 
16404cf0537SLv Zheng 	access_width =
16504cf0537SLv Zheng 	    acpi_hw_get_access_bit_width(*address, reg, max_bit_width);
166920de6ebSLv Zheng 	bit_width =
167920de6ebSLv Zheng 	    ACPI_ROUND_UP(reg->bit_offset + reg->bit_width, access_width);
168920de6ebSLv Zheng 	if (max_bit_width < bit_width) {
169c6b5774cSBob Moore 		ACPI_WARNING((AE_INFO,
170920de6ebSLv Zheng 			      "Requested bit width 0x%X is smaller than register bit width 0x%X",
171920de6ebSLv Zheng 			      max_bit_width, bit_width));
172920de6ebSLv Zheng 		return (AE_SUPPORT);
173c6b5774cSBob Moore 	}
174c6b5774cSBob Moore 
175c6b5774cSBob Moore 	return (AE_OK);
176c6b5774cSBob Moore }
177c6b5774cSBob Moore 
178c6b5774cSBob Moore /******************************************************************************
179c6b5774cSBob Moore  *
180c6b5774cSBob Moore  * FUNCTION:    acpi_hw_read
181c6b5774cSBob Moore  *
182ba494beeSBob Moore  * PARAMETERS:  value               - Where the value is returned
183ba494beeSBob Moore  *              reg                 - GAS register structure
184c6b5774cSBob Moore  *
185c6b5774cSBob Moore  * RETURN:      Status
186c6b5774cSBob Moore  *
1878381c54fSLv Zheng  * DESCRIPTION: Read from either memory or IO space. This is a 64-bit max
1888381c54fSLv Zheng  *              version of acpi_read.
189c6b5774cSBob Moore  *
190c6b5774cSBob Moore  * LIMITATIONS: <These limitations also apply to acpi_hw_write>
191ba494beeSBob Moore  *      space_ID must be system_memory or system_IO.
192c6b5774cSBob Moore  *
193c6b5774cSBob Moore  ******************************************************************************/
194c6b5774cSBob Moore 
acpi_hw_read(u64 * value,struct acpi_generic_address * reg)1958381c54fSLv Zheng acpi_status acpi_hw_read(u64 *value, struct acpi_generic_address *reg)
196c6b5774cSBob Moore {
197c6b5774cSBob Moore 	u64 address;
198c3bc26d4SLv Zheng 	u8 access_width;
199c3bc26d4SLv Zheng 	u32 bit_width;
200c3bc26d4SLv Zheng 	u8 bit_offset;
201653f4b53SBob Moore 	u64 value64;
202c3bc26d4SLv Zheng 	u32 value32;
203c3bc26d4SLv Zheng 	u8 index;
204c6b5774cSBob Moore 	acpi_status status;
205c6b5774cSBob Moore 
206c6b5774cSBob Moore 	ACPI_FUNCTION_NAME(hw_read);
207c6b5774cSBob Moore 
208c6b5774cSBob Moore 	/* Validate contents of the GAS register */
209c6b5774cSBob Moore 
2108381c54fSLv Zheng 	status = acpi_hw_validate_register(reg, 64, &address);
211c6b5774cSBob Moore 	if (ACPI_FAILURE(status)) {
212c6b5774cSBob Moore 		return (status);
213c6b5774cSBob Moore 	}
214c6b5774cSBob Moore 
215c3bc26d4SLv Zheng 	/*
2168381c54fSLv Zheng 	 * Initialize entire 64-bit return value to zero, convert access_width
217c3bc26d4SLv Zheng 	 * into number of bits based
218c3bc26d4SLv Zheng 	 */
219c6b5774cSBob Moore 	*value = 0;
2208381c54fSLv Zheng 	access_width = acpi_hw_get_access_bit_width(address, reg, 64);
221c3bc26d4SLv Zheng 	bit_width = reg->bit_offset + reg->bit_width;
222c3bc26d4SLv Zheng 	bit_offset = reg->bit_offset;
223c6b5774cSBob Moore 
224c6b5774cSBob Moore 	/*
225c6b5774cSBob Moore 	 * Two address spaces supported: Memory or IO. PCI_Config is
226c6b5774cSBob Moore 	 * not supported here because the GAS structure is insufficient
227c6b5774cSBob Moore 	 */
228c3bc26d4SLv Zheng 	index = 0;
229c3bc26d4SLv Zheng 	while (bit_width) {
230c3bc26d4SLv Zheng 		if (bit_offset >= access_width) {
2318381c54fSLv Zheng 			value64 = 0;
232c3bc26d4SLv Zheng 			bit_offset -= access_width;
233c3bc26d4SLv Zheng 		} else {
234c6b5774cSBob Moore 			if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
235c3bc26d4SLv Zheng 				status =
236c3bc26d4SLv Zheng 				    acpi_os_read_memory((acpi_physical_address)
237c3bc26d4SLv Zheng 							address +
238c3bc26d4SLv Zheng 							index *
239c3bc26d4SLv Zheng 							ACPI_DIV_8
240c3bc26d4SLv Zheng 							(access_width),
241c3bc26d4SLv Zheng 							&value64, access_width);
242c6b5774cSBob Moore 			} else {	/* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
243c6b5774cSBob Moore 
244c6b5774cSBob Moore 				status = acpi_hw_read_port((acpi_io_address)
245c3bc26d4SLv Zheng 							   address +
246c3bc26d4SLv Zheng 							   index *
247c3bc26d4SLv Zheng 							   ACPI_DIV_8
248c3bc26d4SLv Zheng 							   (access_width),
249c3bc26d4SLv Zheng 							   &value32,
250c3bc26d4SLv Zheng 							   access_width);
2518381c54fSLv Zheng 				value64 = (u64)value32;
252c3bc26d4SLv Zheng 			}
253c3bc26d4SLv Zheng 		}
254c3bc26d4SLv Zheng 
255c3bc26d4SLv Zheng 		/*
256c3bc26d4SLv Zheng 		 * Use offset style bit writes because "Index * AccessWidth" is
2578381c54fSLv Zheng 		 * ensured to be less than 64-bits by acpi_hw_validate_register().
258c3bc26d4SLv Zheng 		 */
259c3bc26d4SLv Zheng 		ACPI_SET_BITS(value, index * access_width,
2608381c54fSLv Zheng 			      ACPI_MASK_BITS_ABOVE_64(access_width), value64);
261c3bc26d4SLv Zheng 
262c3bc26d4SLv Zheng 		bit_width -=
263c3bc26d4SLv Zheng 		    bit_width > access_width ? access_width : bit_width;
264c3bc26d4SLv Zheng 		index++;
265c6b5774cSBob Moore 	}
266c6b5774cSBob Moore 
267c6b5774cSBob Moore 	ACPI_DEBUG_PRINT((ACPI_DB_IO,
2688381c54fSLv Zheng 			  "Read:  %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n",
2698381c54fSLv Zheng 			  ACPI_FORMAT_UINT64(*value), access_width,
2708381c54fSLv Zheng 			  ACPI_FORMAT_UINT64(address),
271c6b5774cSBob Moore 			  acpi_ut_get_region_name(reg->space_id)));
272c6b5774cSBob Moore 
273c6b5774cSBob Moore 	return (status);
274c6b5774cSBob Moore }
275c6b5774cSBob Moore 
276c6b5774cSBob Moore /******************************************************************************
277c6b5774cSBob Moore  *
278c6b5774cSBob Moore  * FUNCTION:    acpi_hw_write
279c6b5774cSBob Moore  *
280ba494beeSBob Moore  * PARAMETERS:  value               - Value to be written
281ba494beeSBob Moore  *              reg                 - GAS register structure
282c6b5774cSBob Moore  *
283c6b5774cSBob Moore  * RETURN:      Status
284c6b5774cSBob Moore  *
2858381c54fSLv Zheng  * DESCRIPTION: Write to either memory or IO space. This is a 64-bit max
2868381c54fSLv Zheng  *              version of acpi_write.
287c6b5774cSBob Moore  *
288c6b5774cSBob Moore  ******************************************************************************/
289c6b5774cSBob Moore 
acpi_hw_write(u64 value,struct acpi_generic_address * reg)2908381c54fSLv Zheng acpi_status acpi_hw_write(u64 value, struct acpi_generic_address *reg)
291c6b5774cSBob Moore {
292c6b5774cSBob Moore 	u64 address;
293dc4c7376SLv Zheng 	u8 access_width;
294dc4c7376SLv Zheng 	u32 bit_width;
295dc4c7376SLv Zheng 	u8 bit_offset;
296dc4c7376SLv Zheng 	u64 value64;
297dc4c7376SLv Zheng 	u8 index;
298c6b5774cSBob Moore 	acpi_status status;
299c6b5774cSBob Moore 
300c6b5774cSBob Moore 	ACPI_FUNCTION_NAME(hw_write);
301c6b5774cSBob Moore 
302c6b5774cSBob Moore 	/* Validate contents of the GAS register */
303c6b5774cSBob Moore 
3048381c54fSLv Zheng 	status = acpi_hw_validate_register(reg, 64, &address);
305c6b5774cSBob Moore 	if (ACPI_FAILURE(status)) {
306c6b5774cSBob Moore 		return (status);
307c6b5774cSBob Moore 	}
308c6b5774cSBob Moore 
309dc4c7376SLv Zheng 	/* Convert access_width into number of bits based */
310dc4c7376SLv Zheng 
3118381c54fSLv Zheng 	access_width = acpi_hw_get_access_bit_width(address, reg, 64);
312dc4c7376SLv Zheng 	bit_width = reg->bit_offset + reg->bit_width;
313dc4c7376SLv Zheng 	bit_offset = reg->bit_offset;
314dc4c7376SLv Zheng 
315c6b5774cSBob Moore 	/*
316c6b5774cSBob Moore 	 * Two address spaces supported: Memory or IO. PCI_Config is
317c6b5774cSBob Moore 	 * not supported here because the GAS structure is insufficient
318c6b5774cSBob Moore 	 */
319dc4c7376SLv Zheng 	index = 0;
320dc4c7376SLv Zheng 	while (bit_width) {
321dc4c7376SLv Zheng 		/*
322dc4c7376SLv Zheng 		 * Use offset style bit reads because "Index * AccessWidth" is
3238381c54fSLv Zheng 		 * ensured to be less than 64-bits by acpi_hw_validate_register().
324dc4c7376SLv Zheng 		 */
3258381c54fSLv Zheng 		value64 = ACPI_GET_BITS(&value, index * access_width,
3268381c54fSLv Zheng 					ACPI_MASK_BITS_ABOVE_64(access_width));
327dc4c7376SLv Zheng 
328dc4c7376SLv Zheng 		if (bit_offset >= access_width) {
329dc4c7376SLv Zheng 			bit_offset -= access_width;
330dc4c7376SLv Zheng 		} else {
331c6b5774cSBob Moore 			if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
332dc4c7376SLv Zheng 				status =
333dc4c7376SLv Zheng 				    acpi_os_write_memory((acpi_physical_address)
334dc4c7376SLv Zheng 							 address +
335dc4c7376SLv Zheng 							 index *
336dc4c7376SLv Zheng 							 ACPI_DIV_8
337dc4c7376SLv Zheng 							 (access_width),
338dc4c7376SLv Zheng 							 value64, access_width);
339c6b5774cSBob Moore 			} else {	/* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
340c6b5774cSBob Moore 
341c6b5774cSBob Moore 				status = acpi_hw_write_port((acpi_io_address)
342dc4c7376SLv Zheng 							    address +
343dc4c7376SLv Zheng 							    index *
344dc4c7376SLv Zheng 							    ACPI_DIV_8
345dc4c7376SLv Zheng 							    (access_width),
3468381c54fSLv Zheng 							    (u32)value64,
347dc4c7376SLv Zheng 							    access_width);
348dc4c7376SLv Zheng 			}
349dc4c7376SLv Zheng 		}
350dc4c7376SLv Zheng 
351dc4c7376SLv Zheng 		/*
352dc4c7376SLv Zheng 		 * Index * access_width is ensured to be less than 32-bits by
353dc4c7376SLv Zheng 		 * acpi_hw_validate_register().
354dc4c7376SLv Zheng 		 */
355dc4c7376SLv Zheng 		bit_width -=
356dc4c7376SLv Zheng 		    bit_width > access_width ? access_width : bit_width;
357dc4c7376SLv Zheng 		index++;
358c6b5774cSBob Moore 	}
359c6b5774cSBob Moore 
360c6b5774cSBob Moore 	ACPI_DEBUG_PRINT((ACPI_DB_IO,
3618381c54fSLv Zheng 			  "Wrote: %8.8X%8.8X width %2d   to %8.8X%8.8X (%s)\n",
3628381c54fSLv Zheng 			  ACPI_FORMAT_UINT64(value), access_width,
3638381c54fSLv Zheng 			  ACPI_FORMAT_UINT64(address),
364c6b5774cSBob Moore 			  acpi_ut_get_region_name(reg->space_id)));
365c6b5774cSBob Moore 
366c6b5774cSBob Moore 	return (status);
367c6b5774cSBob Moore }
368c6b5774cSBob Moore 
36933620c54SBob Moore #if (!ACPI_REDUCED_HARDWARE)
37095b482a8SLen Brown /*******************************************************************************
37195b482a8SLen Brown  *
37295b482a8SLen Brown  * FUNCTION:    acpi_hw_clear_acpi_status
37395b482a8SLen Brown  *
37495b482a8SLen Brown  * PARAMETERS:  None
37595b482a8SLen Brown  *
37695b482a8SLen Brown  * RETURN:      Status
37795b482a8SLen Brown  *
37895b482a8SLen Brown  * DESCRIPTION: Clears all fixed and general purpose status bits
37995b482a8SLen Brown  *
38095b482a8SLen Brown  ******************************************************************************/
381c520abadSBob Moore 
acpi_hw_clear_acpi_status(void)38295b482a8SLen Brown acpi_status acpi_hw_clear_acpi_status(void)
38395b482a8SLen Brown {
38495b482a8SLen Brown 	acpi_status status;
38595b482a8SLen Brown 	acpi_cpu_flags lock_flags = 0;
38695b482a8SLen Brown 
38795b482a8SLen Brown 	ACPI_FUNCTION_TRACE(hw_clear_acpi_status);
38895b482a8SLen Brown 
3898eb7b247SBob Moore 	ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
39095b482a8SLen Brown 			  ACPI_BITMASK_ALL_FIXED_STATUS,
3918eb7b247SBob Moore 			  ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address)));
39295b482a8SLen Brown 
393c57c0ad4SSteven Rostedt 	lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
39495b482a8SLen Brown 
395227243a0SBob Moore 	/* Clear the fixed events in PM1 A/B */
396531c633dSBob Moore 
39795b482a8SLen Brown 	status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
39895b482a8SLen Brown 					ACPI_BITMASK_ALL_FIXED_STATUS);
399f7f71cfbSRakib Mullick 
400c57c0ad4SSteven Rostedt 	acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
401f7f71cfbSRakib Mullick 
4027d3e83bdSLv Zheng 	if (ACPI_FAILURE(status)) {
403f7f71cfbSRakib Mullick 		goto exit;
4047d3e83bdSLv Zheng 	}
40595b482a8SLen Brown 
40695b482a8SLen Brown 	/* Clear the GPE Bits in all GPE registers in all GPE blocks */
40795b482a8SLen Brown 
40895b482a8SLen Brown 	status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
40995b482a8SLen Brown 
410f7f71cfbSRakib Mullick exit:
41195b482a8SLen Brown 	return_ACPI_STATUS(status);
41295b482a8SLen Brown }
41395b482a8SLen Brown 
41495b482a8SLen Brown /*******************************************************************************
41595b482a8SLen Brown  *
41633620c54SBob Moore  * FUNCTION:    acpi_hw_get_bit_register_info
41795b482a8SLen Brown  *
41895b482a8SLen Brown  * PARAMETERS:  register_id         - Index of ACPI Register to access
41995b482a8SLen Brown  *
42095b482a8SLen Brown  * RETURN:      The bitmask to be used when accessing the register
42195b482a8SLen Brown  *
42295b482a8SLen Brown  * DESCRIPTION: Map register_id into a register bitmask.
42395b482a8SLen Brown  *
42495b482a8SLen Brown  ******************************************************************************/
42595b482a8SLen Brown 
acpi_hw_get_bit_register_info(u32 register_id)42695b482a8SLen Brown struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id)
42795b482a8SLen Brown {
42895b482a8SLen Brown 	ACPI_FUNCTION_ENTRY();
42995b482a8SLen Brown 
43095b482a8SLen Brown 	if (register_id > ACPI_BITREG_MAX) {
431f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: 0x%X",
43295b482a8SLen Brown 			    register_id));
43395b482a8SLen Brown 		return (NULL);
43495b482a8SLen Brown 	}
43595b482a8SLen Brown 
43695b482a8SLen Brown 	return (&acpi_gbl_bit_register_info[register_id]);
43795b482a8SLen Brown }
43895b482a8SLen Brown 
43995b482a8SLen Brown /******************************************************************************
44095b482a8SLen Brown  *
44132c9ef99SBob Moore  * FUNCTION:    acpi_hw_write_pm1_control
44232c9ef99SBob Moore  *
44332c9ef99SBob Moore  * PARAMETERS:  pm1a_control        - Value to be written to PM1A control
44432c9ef99SBob Moore  *              pm1b_control        - Value to be written to PM1B control
44532c9ef99SBob Moore  *
44632c9ef99SBob Moore  * RETURN:      Status
44732c9ef99SBob Moore  *
44832c9ef99SBob Moore  * DESCRIPTION: Write the PM1 A/B control registers. These registers are
449*1cf0cee1STom Rix  *              different than the PM1 A/B status and enable registers
45032c9ef99SBob Moore  *              in that different values can be written to the A/B registers.
45132c9ef99SBob Moore  *              Most notably, the SLP_TYP bits can be different, as per the
45232c9ef99SBob Moore  *              values returned from the _Sx predefined methods.
45332c9ef99SBob Moore  *
45432c9ef99SBob Moore  ******************************************************************************/
45532c9ef99SBob Moore 
acpi_hw_write_pm1_control(u32 pm1a_control,u32 pm1b_control)45632c9ef99SBob Moore acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control)
45732c9ef99SBob Moore {
45832c9ef99SBob Moore 	acpi_status status;
45932c9ef99SBob Moore 
46032c9ef99SBob Moore 	ACPI_FUNCTION_TRACE(hw_write_pm1_control);
46132c9ef99SBob Moore 
462c6b5774cSBob Moore 	status =
463c6b5774cSBob Moore 	    acpi_hw_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block);
46432c9ef99SBob Moore 	if (ACPI_FAILURE(status)) {
46532c9ef99SBob Moore 		return_ACPI_STATUS(status);
46632c9ef99SBob Moore 	}
46732c9ef99SBob Moore 
46832c9ef99SBob Moore 	if (acpi_gbl_FADT.xpm1b_control_block.address) {
46932c9ef99SBob Moore 		status =
470c6b5774cSBob Moore 		    acpi_hw_write(pm1b_control,
47132c9ef99SBob Moore 				  &acpi_gbl_FADT.xpm1b_control_block);
47232c9ef99SBob Moore 	}
47332c9ef99SBob Moore 	return_ACPI_STATUS(status);
47432c9ef99SBob Moore }
47532c9ef99SBob Moore 
47632c9ef99SBob Moore /******************************************************************************
47732c9ef99SBob Moore  *
47895b482a8SLen Brown  * FUNCTION:    acpi_hw_register_read
47995b482a8SLen Brown  *
48095b482a8SLen Brown  * PARAMETERS:  register_id         - ACPI Register ID
48195b482a8SLen Brown  *              return_value        - Where the register value is returned
48295b482a8SLen Brown  *
48395b482a8SLen Brown  * RETURN:      Status and the value read.
48495b482a8SLen Brown  *
48595b482a8SLen Brown  * DESCRIPTION: Read from the specified ACPI register
48695b482a8SLen Brown  *
48795b482a8SLen Brown  ******************************************************************************/
acpi_hw_register_read(u32 register_id,u32 * return_value)4883e8214e5SLv Zheng acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value)
48995b482a8SLen Brown {
490c520abadSBob Moore 	u32 value = 0;
4918381c54fSLv Zheng 	u64 value64;
49295b482a8SLen Brown 	acpi_status status;
49395b482a8SLen Brown 
49495b482a8SLen Brown 	ACPI_FUNCTION_TRACE(hw_register_read);
49595b482a8SLen Brown 
49695b482a8SLen Brown 	switch (register_id) {
497c520abadSBob Moore 	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
49895b482a8SLen Brown 
499c520abadSBob Moore 		status = acpi_hw_read_multiple(&value,
500c520abadSBob Moore 					       &acpi_gbl_xpm1a_status,
501c520abadSBob Moore 					       &acpi_gbl_xpm1b_status);
50295b482a8SLen Brown 		break;
50395b482a8SLen Brown 
504c520abadSBob Moore 	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access each */
50595b482a8SLen Brown 
506c520abadSBob Moore 		status = acpi_hw_read_multiple(&value,
507c520abadSBob Moore 					       &acpi_gbl_xpm1a_enable,
508c520abadSBob Moore 					       &acpi_gbl_xpm1b_enable);
50995b482a8SLen Brown 		break;
51095b482a8SLen Brown 
511c520abadSBob Moore 	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
51295b482a8SLen Brown 
513c520abadSBob Moore 		status = acpi_hw_read_multiple(&value,
514c520abadSBob Moore 					       &acpi_gbl_FADT.
515c520abadSBob Moore 					       xpm1a_control_block,
516c520abadSBob Moore 					       &acpi_gbl_FADT.
517c520abadSBob Moore 					       xpm1b_control_block);
518c3dd25f4SLin Ming 
519c3dd25f4SLin Ming 		/*
520c3dd25f4SLin Ming 		 * Zero the write-only bits. From the ACPI specification, "Hardware
521c3dd25f4SLin Ming 		 * Write-Only Bits": "Upon reads to registers with write-only bits,
522c3dd25f4SLin Ming 		 * software masks out all write-only bits."
523c3dd25f4SLin Ming 		 */
524c3dd25f4SLin Ming 		value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
52595b482a8SLen Brown 		break;
52695b482a8SLen Brown 
52795b482a8SLen Brown 	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
52895b482a8SLen Brown 
529c6b5774cSBob Moore 		status =
5308381c54fSLv Zheng 		    acpi_hw_read(&value64, &acpi_gbl_FADT.xpm2_control_block);
531f016b19aSErik Schmauss 		if (ACPI_SUCCESS(status)) {
5328381c54fSLv Zheng 			value = (u32)value64;
533f016b19aSErik Schmauss 		}
53495b482a8SLen Brown 		break;
53595b482a8SLen Brown 
53695b482a8SLen Brown 	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
53795b482a8SLen Brown 
5388381c54fSLv Zheng 		status = acpi_hw_read(&value64, &acpi_gbl_FADT.xpm_timer_block);
539f016b19aSErik Schmauss 		if (ACPI_SUCCESS(status)) {
5408381c54fSLv Zheng 			value = (u32)value64;
541f016b19aSErik Schmauss 		}
542f016b19aSErik Schmauss 
54395b482a8SLen Brown 		break;
54495b482a8SLen Brown 
54595b482a8SLen Brown 	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
54695b482a8SLen Brown 
54795b482a8SLen Brown 		status =
5487f071903SBob Moore 		    acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8);
54995b482a8SLen Brown 		break;
55095b482a8SLen Brown 
55195b482a8SLen Brown 	default:
5521d1ea1b7SChao Guan 
553f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
55495b482a8SLen Brown 		status = AE_BAD_PARAMETER;
55595b482a8SLen Brown 		break;
55695b482a8SLen Brown 	}
55795b482a8SLen Brown 
55895b482a8SLen Brown 	if (ACPI_SUCCESS(status)) {
5598381c54fSLv Zheng 		*return_value = (u32)value;
56095b482a8SLen Brown 	}
56195b482a8SLen Brown 
56295b482a8SLen Brown 	return_ACPI_STATUS(status);
56395b482a8SLen Brown }
56495b482a8SLen Brown 
56595b482a8SLen Brown /******************************************************************************
56695b482a8SLen Brown  *
56795b482a8SLen Brown  * FUNCTION:    acpi_hw_register_write
56895b482a8SLen Brown  *
56995b482a8SLen Brown  * PARAMETERS:  register_id         - ACPI Register ID
570ba494beeSBob Moore  *              value               - The value to write
57195b482a8SLen Brown  *
57295b482a8SLen Brown  * RETURN:      Status
57395b482a8SLen Brown  *
57495b482a8SLen Brown  * DESCRIPTION: Write to the specified ACPI register
57595b482a8SLen Brown  *
57695b482a8SLen Brown  * NOTE: In accordance with the ACPI specification, this function automatically
57795b482a8SLen Brown  * preserves the value of the following bits, meaning that these bits cannot be
57895b482a8SLen Brown  * changed via this interface:
57995b482a8SLen Brown  *
58095b482a8SLen Brown  * PM1_CONTROL[0] = SCI_EN
58195b482a8SLen Brown  * PM1_CONTROL[9]
58295b482a8SLen Brown  * PM1_STATUS[11]
58395b482a8SLen Brown  *
58495b482a8SLen Brown  * ACPI References:
58595b482a8SLen Brown  * 1) Hardware Ignored Bits: When software writes to a register with ignored
58695b482a8SLen Brown  *      bit fields, it preserves the ignored bit fields
58795b482a8SLen Brown  * 2) SCI_EN: OSPM always preserves this bit position
58895b482a8SLen Brown  *
58995b482a8SLen Brown  ******************************************************************************/
59095b482a8SLen Brown 
acpi_hw_register_write(u32 register_id,u32 value)59195b482a8SLen Brown acpi_status acpi_hw_register_write(u32 register_id, u32 value)
59295b482a8SLen Brown {
59395b482a8SLen Brown 	acpi_status status;
59495b482a8SLen Brown 	u32 read_value;
5958381c54fSLv Zheng 	u64 read_value64;
59695b482a8SLen Brown 
59795b482a8SLen Brown 	ACPI_FUNCTION_TRACE(hw_register_write);
59895b482a8SLen Brown 
59995b482a8SLen Brown 	switch (register_id) {
600c520abadSBob Moore 	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
6018636f8d2SBob Moore 		/*
6028636f8d2SBob Moore 		 * Handle the "ignored" bit in PM1 Status. According to the ACPI
6038636f8d2SBob Moore 		 * specification, ignored bits are to be preserved when writing.
6048636f8d2SBob Moore 		 * Normally, this would mean a read/modify/write sequence. However,
6058636f8d2SBob Moore 		 * preserving a bit in the status register is different. Writing a
6068636f8d2SBob Moore 		 * one clears the status, and writing a zero preserves the status.
6078636f8d2SBob Moore 		 * Therefore, we must always write zero to the ignored bit.
6088636f8d2SBob Moore 		 *
6098636f8d2SBob Moore 		 * This behavior is clarified in the ACPI 4.0 specification.
6108636f8d2SBob Moore 		 */
6118636f8d2SBob Moore 		value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
61295b482a8SLen Brown 
613c520abadSBob Moore 		status = acpi_hw_write_multiple(value,
614c520abadSBob Moore 						&acpi_gbl_xpm1a_status,
615c520abadSBob Moore 						&acpi_gbl_xpm1b_status);
61695b482a8SLen Brown 		break;
61795b482a8SLen Brown 
61875c8044fSLv Zheng 	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access each */
61995b482a8SLen Brown 
620c520abadSBob Moore 		status = acpi_hw_write_multiple(value,
621c520abadSBob Moore 						&acpi_gbl_xpm1a_enable,
622c520abadSBob Moore 						&acpi_gbl_xpm1b_enable);
62395b482a8SLen Brown 		break;
62495b482a8SLen Brown 
625c520abadSBob Moore 	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
62695b482a8SLen Brown 		/*
62795b482a8SLen Brown 		 * Perform a read first to preserve certain bits (per ACPI spec)
628c520abadSBob Moore 		 * Note: This includes SCI_EN, we never want to change this bit
62995b482a8SLen Brown 		 */
630c520abadSBob Moore 		status = acpi_hw_read_multiple(&read_value,
631c520abadSBob Moore 					       &acpi_gbl_FADT.
632c520abadSBob Moore 					       xpm1a_control_block,
633c520abadSBob Moore 					       &acpi_gbl_FADT.
634c520abadSBob Moore 					       xpm1b_control_block);
63595b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
63695b482a8SLen Brown 			goto exit;
63795b482a8SLen Brown 		}
63895b482a8SLen Brown 
63995b482a8SLen Brown 		/* Insert the bits to be preserved */
64095b482a8SLen Brown 
64195b482a8SLen Brown 		ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS,
64295b482a8SLen Brown 				 read_value);
64395b482a8SLen Brown 
64495b482a8SLen Brown 		/* Now we can write the data */
64595b482a8SLen Brown 
646c520abadSBob Moore 		status = acpi_hw_write_multiple(value,
647c520abadSBob Moore 						&acpi_gbl_FADT.
648c520abadSBob Moore 						xpm1a_control_block,
649c520abadSBob Moore 						&acpi_gbl_FADT.
650c520abadSBob Moore 						xpm1b_control_block);
65195b482a8SLen Brown 		break;
65295b482a8SLen Brown 
65395b482a8SLen Brown 	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
65420869dcfSBob Moore 		/*
65520869dcfSBob Moore 		 * For control registers, all reserved bits must be preserved,
65620869dcfSBob Moore 		 * as per the ACPI spec.
65720869dcfSBob Moore 		 */
65820869dcfSBob Moore 		status =
6598381c54fSLv Zheng 		    acpi_hw_read(&read_value64,
660c6b5774cSBob Moore 				 &acpi_gbl_FADT.xpm2_control_block);
66120869dcfSBob Moore 		if (ACPI_FAILURE(status)) {
66220869dcfSBob Moore 			goto exit;
66320869dcfSBob Moore 		}
6648381c54fSLv Zheng 		read_value = (u32)read_value64;
66520869dcfSBob Moore 
66620869dcfSBob Moore 		/* Insert the bits to be preserved */
66720869dcfSBob Moore 
66820869dcfSBob Moore 		ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS,
66920869dcfSBob Moore 				 read_value);
67020869dcfSBob Moore 
671c6b5774cSBob Moore 		status =
672c6b5774cSBob Moore 		    acpi_hw_write(value, &acpi_gbl_FADT.xpm2_control_block);
67395b482a8SLen Brown 		break;
67495b482a8SLen Brown 
67595b482a8SLen Brown 	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
67695b482a8SLen Brown 
677c6b5774cSBob Moore 		status = acpi_hw_write(value, &acpi_gbl_FADT.xpm_timer_block);
67895b482a8SLen Brown 		break;
67995b482a8SLen Brown 
68095b482a8SLen Brown 	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
68195b482a8SLen Brown 
68295b482a8SLen Brown 		/* SMI_CMD is currently always in IO space */
68395b482a8SLen Brown 
68495b482a8SLen Brown 		status =
6857f071903SBob Moore 		    acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8);
68695b482a8SLen Brown 		break;
68795b482a8SLen Brown 
68895b482a8SLen Brown 	default:
6891d1ea1b7SChao Guan 
690f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id));
69195b482a8SLen Brown 		status = AE_BAD_PARAMETER;
69295b482a8SLen Brown 		break;
69395b482a8SLen Brown 	}
69495b482a8SLen Brown 
69595b482a8SLen Brown exit:
69695b482a8SLen Brown 	return_ACPI_STATUS(status);
69795b482a8SLen Brown }
698c520abadSBob Moore 
699c520abadSBob Moore /******************************************************************************
700c520abadSBob Moore  *
701c520abadSBob Moore  * FUNCTION:    acpi_hw_read_multiple
702c520abadSBob Moore  *
703ba494beeSBob Moore  * PARAMETERS:  value               - Where the register value is returned
704c520abadSBob Moore  *              register_a           - First ACPI register (required)
705c520abadSBob Moore  *              register_b           - Second ACPI register (optional)
706c520abadSBob Moore  *
707c520abadSBob Moore  * RETURN:      Status
708c520abadSBob Moore  *
709c520abadSBob Moore  * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
710c520abadSBob Moore  *
711c520abadSBob Moore  ******************************************************************************/
712c520abadSBob Moore 
713c520abadSBob Moore static acpi_status
acpi_hw_read_multiple(u32 * value,struct acpi_generic_address * register_a,struct acpi_generic_address * register_b)714c520abadSBob Moore acpi_hw_read_multiple(u32 *value,
715c520abadSBob Moore 		      struct acpi_generic_address *register_a,
716c520abadSBob Moore 		      struct acpi_generic_address *register_b)
717c520abadSBob Moore {
718c520abadSBob Moore 	u32 value_a = 0;
719c520abadSBob Moore 	u32 value_b = 0;
7208381c54fSLv Zheng 	u64 value64;
721c520abadSBob Moore 	acpi_status status;
722c520abadSBob Moore 
723c520abadSBob Moore 	/* The first register is always required */
724c520abadSBob Moore 
7258381c54fSLv Zheng 	status = acpi_hw_read(&value64, register_a);
726c520abadSBob Moore 	if (ACPI_FAILURE(status)) {
727c520abadSBob Moore 		return (status);
728c520abadSBob Moore 	}
7298381c54fSLv Zheng 	value_a = (u32)value64;
730c520abadSBob Moore 
731c520abadSBob Moore 	/* Second register is optional */
732c520abadSBob Moore 
733c520abadSBob Moore 	if (register_b->address) {
7348381c54fSLv Zheng 		status = acpi_hw_read(&value64, register_b);
735c520abadSBob Moore 		if (ACPI_FAILURE(status)) {
736c520abadSBob Moore 			return (status);
737c520abadSBob Moore 		}
7388381c54fSLv Zheng 		value_b = (u32)value64;
739c520abadSBob Moore 	}
740c520abadSBob Moore 
741aefc7f9aSBob Moore 	/*
742aefc7f9aSBob Moore 	 * OR the two return values together. No shifting or masking is necessary,
743aefc7f9aSBob Moore 	 * because of how the PM1 registers are defined in the ACPI specification:
744aefc7f9aSBob Moore 	 *
745aefc7f9aSBob Moore 	 * "Although the bits can be split between the two register blocks (each
746aefc7f9aSBob Moore 	 * register block has a unique pointer within the FADT), the bit positions
747aefc7f9aSBob Moore 	 * are maintained. The register block with unimplemented bits (that is,
748aefc7f9aSBob Moore 	 * those implemented in the other register block) always returns zeros,
749aefc7f9aSBob Moore 	 * and writes have no side effects"
750aefc7f9aSBob Moore 	 */
751aefc7f9aSBob Moore 	*value = (value_a | value_b);
752c520abadSBob Moore 	return (AE_OK);
753c520abadSBob Moore }
754c520abadSBob Moore 
755c520abadSBob Moore /******************************************************************************
756c520abadSBob Moore  *
757c520abadSBob Moore  * FUNCTION:    acpi_hw_write_multiple
758c520abadSBob Moore  *
759ba494beeSBob Moore  * PARAMETERS:  value               - The value to write
760c520abadSBob Moore  *              register_a           - First ACPI register (required)
761c520abadSBob Moore  *              register_b           - Second ACPI register (optional)
762c520abadSBob Moore  *
763c520abadSBob Moore  * RETURN:      Status
764c520abadSBob Moore  *
765c520abadSBob Moore  * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
766c520abadSBob Moore  *
767c520abadSBob Moore  ******************************************************************************/
768c520abadSBob Moore 
769c520abadSBob Moore static acpi_status
acpi_hw_write_multiple(u32 value,struct acpi_generic_address * register_a,struct acpi_generic_address * register_b)770c520abadSBob Moore acpi_hw_write_multiple(u32 value,
771c520abadSBob Moore 		       struct acpi_generic_address *register_a,
772c520abadSBob Moore 		       struct acpi_generic_address *register_b)
773c520abadSBob Moore {
774c520abadSBob Moore 	acpi_status status;
775c520abadSBob Moore 
776c520abadSBob Moore 	/* The first register is always required */
777c520abadSBob Moore 
778c6b5774cSBob Moore 	status = acpi_hw_write(value, register_a);
779c520abadSBob Moore 	if (ACPI_FAILURE(status)) {
780c520abadSBob Moore 		return (status);
781c520abadSBob Moore 	}
782c520abadSBob Moore 
783aefc7f9aSBob Moore 	/*
784aefc7f9aSBob Moore 	 * Second register is optional
785aefc7f9aSBob Moore 	 *
786aefc7f9aSBob Moore 	 * No bit shifting or clearing is necessary, because of how the PM1
787aefc7f9aSBob Moore 	 * registers are defined in the ACPI specification:
788aefc7f9aSBob Moore 	 *
789aefc7f9aSBob Moore 	 * "Although the bits can be split between the two register blocks (each
790aefc7f9aSBob Moore 	 * register block has a unique pointer within the FADT), the bit positions
791aefc7f9aSBob Moore 	 * are maintained. The register block with unimplemented bits (that is,
792aefc7f9aSBob Moore 	 * those implemented in the other register block) always returns zeros,
793aefc7f9aSBob Moore 	 * and writes have no side effects"
794aefc7f9aSBob Moore 	 */
795c520abadSBob Moore 	if (register_b->address) {
796c6b5774cSBob Moore 		status = acpi_hw_write(value, register_b);
797c520abadSBob Moore 	}
798c520abadSBob Moore 
799c520abadSBob Moore 	return (status);
800c520abadSBob Moore }
80133620c54SBob Moore 
80233620c54SBob Moore #endif				/* !ACPI_REDUCED_HARDWARE */
803