]> xenbits.xensource.com Git - people/hx242/xen.git/commitdiff
x86/acpi: use FADT flags to determine the PMTMR width
authorGrzegorz Uriasz <gorbak25@gmail.com>
Thu, 25 Jun 2020 07:11:09 +0000 (09:11 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 25 Jun 2020 07:11:09 +0000 (09:11 +0200)
On some computers the bit width of the PM Timer as reported
by ACPI is 32 bits when in fact the FADT flags report correctly
that the timer is 24 bits wide. On affected machines such as the
ASUS FX504GM and never gaming laptops this results in the inability
to resume the machine from suspend. Without this patch suspend is
broken on affected machines and even if a machine manages to resume
correctly then the kernel time and xen timers are trashed.

Signed-off-by: Grzegorz Uriasz <gorbak25@gmail.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Release-acked-by: Paul Durrant <paul@xen.org>
xen/arch/x86/acpi/boot.c
xen/arch/x86/time.c
xen/include/acpi/acmacros.h

index bcba52e232e5c46ee713024989e84bf52d93b805..ed40ce56b4efa5688761fdcfd2f69c9414509639 100644 (file)
@@ -475,10 +475,17 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table)
 
 #ifdef CONFIG_X86_PM_TIMER
        /* detect the location of the ACPI PM Timer */
-       if (fadt->header.revision >= FADT2_REVISION_ID) {
+       if (fadt->header.revision >= FADT2_REVISION_ID &&
+           fadt->xpm_timer_block.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
                /* FADT rev. 2 */
-               if (fadt->xpm_timer_block.space_id ==
-                   ACPI_ADR_SPACE_SYSTEM_IO) {
+               if (fadt->xpm_timer_block.access_width != 0 &&
+                   ACPI_ACCESS_BIT_WIDTH(fadt->xpm_timer_block.access_width) != 32)
+                       printk(KERN_WARNING PREFIX "PM-Timer has invalid access width(%u)\n",
+                              fadt->xpm_timer_block.access_width);
+               else if (fadt->xpm_timer_block.bit_offset != 0)
+                       printk(KERN_WARNING PREFIX "PM-Timer has invalid bit offset(%u)\n",
+                              fadt->xpm_timer_block.bit_offset);
+               else {
                        pmtmr_ioport = fadt->xpm_timer_block.address;
                        pmtmr_width = fadt->xpm_timer_block.bit_width;
                }
@@ -490,8 +497,12 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table)
         */
        if (!pmtmr_ioport) {
                pmtmr_ioport = fadt->pm_timer_block;
-               pmtmr_width = fadt->pm_timer_length == 4 ? 24 : 0;
+               pmtmr_width = fadt->pm_timer_length == 4 ? 32 : 0;
        }
+       if (pmtmr_width < 32 && (fadt->flags & ACPI_FADT_32BIT_TIMER))
+               printk(KERN_WARNING PREFIX "PM-Timer is too short\n");
+       if (pmtmr_width > 24 && !(fadt->flags & ACPI_FADT_32BIT_TIMER))
+               pmtmr_width = 24;
        if (pmtmr_ioport)
                printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x (%u bits)\n",
                       pmtmr_ioport, pmtmr_width);
index d643590c0af69d940664dbe4830a15ae50d55889..cf97dde9d6894a21d8011a518ba69d6c8e3ff6b3 100644 (file)
@@ -457,16 +457,13 @@ static u64 read_pmtimer_count(void)
 static s64 __init init_pmtimer(struct platform_timesource *pts)
 {
     u64 start;
-    u32 count, target, mask = 0xffffff;
+    u32 count, target, mask;
 
-    if ( !pmtmr_ioport || !pmtmr_width )
+    if ( !pmtmr_ioport || (pmtmr_width != 24 && pmtmr_width != 32) )
         return 0;
 
-    if ( pmtmr_width == 32 )
-    {
-        pts->counter_bits = 32;
-        mask = 0xffffffff;
-    }
+    pts->counter_bits = pmtmr_width;
+    mask = 0xffffffff >> (32 - pmtmr_width);
 
     count = inl(pmtmr_ioport) & mask;
     start = rdtsc_ordered();
@@ -486,7 +483,6 @@ static struct platform_timesource __initdata plt_pmtimer =
     .name = "ACPI PM Timer",
     .frequency = ACPI_PM_FREQUENCY,
     .read_counter = read_pmtimer_count,
-    .counter_bits = 24,
     .init = init_pmtimer
 };
 
index 6765535053fd58b7d4bf3e2372d7f54db60551f7..86c503c20f3b736a2046f8c5a6ce5d827f51d297 100644 (file)
 #define ACPI_COMPARE_NAME(a,b)          (!ACPI_STRNCMP (ACPI_CAST_PTR (char,(a)), ACPI_CAST_PTR (char,(b)), ACPI_NAME_SIZE))
 #endif
 
+/*
+ * Algorithm to obtain access bit or byte width.
+ * Can be used with access_width of struct acpi_generic_address and access_size of
+ * struct acpi_resource_generic_register.
+ */
+#define ACPI_ACCESS_BIT_WIDTH(size)     (1 << ((size) + 2))
+#define ACPI_ACCESS_BYTE_WIDTH(size)    (1 << ((size) - 1))
+
 /*
  * Macros for moving data around to/from buffers that are possibly unaligned.
  * If the hardware supports the transfer of unaligned data, just do the store.