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, ®->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