Bring in YONETANI Tomokazu's acpi-update-2.patch (27-May-2004), a major
[dragonfly.git] / sys / dev / acpica5 / acpi_timer.c
index a0befe4..4e4f9eb 100644 (file)
@@ -24,8 +24,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/acpica/acpi_timer.c,v 1.25 2003/08/28 16:06:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_timer.c,v 1.1 2004/02/21 06:48:08 dillon Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_timer.c,v 1.32 2004/04/24 16:25:00 njl Exp $
+ * $DragonFly: src/sys/dev/acpica5/acpi_timer.c,v 1.2 2004/06/27 08:52:39 dillon Exp $
  */
 #include "opt_acpi.h"
 #include <sys/param.h>
  */
 
 /* Hooks for the ACPI CA debugging infrastructure */
-#define _COMPONENT     ACPI_SYSTEM
+#define _COMPONENT     ACPI_TIMER
 ACPI_MODULE_NAME("TIMER")
 
-static device_t        acpi_timer_dev;
-struct resource        *acpi_timer_reg;
+static device_t                        acpi_timer_dev;
+static struct resource         *acpi_timer_reg;
+static bus_space_handle_t      acpi_timer_bsh;
+static bus_space_tag_t         acpi_timer_bst;
 
 static u_int   acpi_timer_frequency = 14318182 / 4;
 
 static void    acpi_timer_identify(driver_t *driver, device_t parent);
 static int     acpi_timer_probe(device_t dev);
 static int     acpi_timer_attach(device_t dev);
-static unsigned        acpi_timer_get_timecount(struct timecounter *tc);
-static unsigned        acpi_timer_get_timecount_safe(struct timecounter *tc);
+static u_int   acpi_timer_get_timecount(struct timecounter *tc);
+static u_int   acpi_timer_get_timecount_safe(struct timecounter *tc);
 static int     acpi_timer_sysctl_freq(SYSCTL_HANDLER_ARGS);
-static void    acpi_timer_test(void);
+static void    acpi_timer_boot_test(void);
 
-static uint32_t read_counter(void);
-static int     test_counter(void);
+static u_int   acpi_timer_read(void);
+static int     acpi_timer_test(void);
 
 static device_method_t acpi_timer_methods[] = {
     DEVMETHOD(device_identify, acpi_timer_identify),
@@ -90,66 +92,23 @@ static driver_t acpi_timer_driver = {
 
 static devclass_t acpi_timer_devclass;
 DRIVER_MODULE(acpi_timer, acpi, acpi_timer_driver, acpi_timer_devclass, 0, 0);
+MODULE_DEPEND(acpi_timer, acpi, 1, 1, 1);
 
 static struct timecounter acpi_timer_timecounter = {
-       acpi_timer_get_timecount_safe,
-       0,
-       0xffffff,
-       0,
-       "ACPI",
-       1000
+       acpi_timer_get_timecount_safe,  /* get_timecount function */
+       0,                              /* no poll_pps */
+       0,                              /* no default counter_mask */
+       0,                              /* no default frequency */
+       "ACPI",                         /* name */
+       1000                            /* quality */
 };
 
-static uint32_t
-read_counter()
+static u_int
+acpi_timer_read()
 {
-    bus_space_handle_t bsh;
-    bus_space_tag_t bst;
-    u_int32_t tv;
-
-    bsh = rman_get_bushandle(acpi_timer_reg);
-    bst = rman_get_bustag(acpi_timer_reg);
-    tv = bus_space_read_4(bst, bsh, 0);
-    bus_space_barrier(bst, bsh, 0, 4, BUS_SPACE_BARRIER_READ);
-
-    return (tv);
+    return (bus_space_read_4(acpi_timer_bst, acpi_timer_bsh, 0));
 }
 
-#define N 2000
-static int
-test_counter()
-{
-    u_int      last, this;
-    int                min, max, n, delta;
-
-    min = 10000000;
-    max = 0;
-    last = read_counter();
-    for (n = 0; n < N; n++) {
-       this = read_counter();
-       delta = (this - last) & 0xffffff;
-       if (delta > max)
-           max = delta;
-       else if (delta < min)
-           min = delta;
-       last = this;
-    }
-    if (max - min > 2)
-       n = 0;
-    else if (min < 0 || max == 0)
-       n = 0;
-    else
-       n = 1;
-    if (bootverbose) {
-       printf("ACPI timer looks %s min = %d, max = %d, width = %d\n",
-               n ? "GOOD" : "BAD ",
-               min, max, max - min);
-    }
-
-    return (n);
-}
-#undef N
-
 /*
  * Locate the ACPI timer using the FADT, set up and allocate the I/O resources
  * we will be using.
@@ -166,7 +125,7 @@ acpi_timer_identify(driver_t *driver, device_t parent)
 
     if (acpi_disabled("timer") || AcpiGbl_FADT == NULL)
        return_VOID;
-    
+
     if ((dev = BUS_ADD_CHILD(parent, 0, "acpi_timer", 0)) == NULL) {
        device_printf(parent, "could not add acpi_timer0\n");
        return_VOID;
@@ -179,19 +138,30 @@ acpi_timer_identify(driver_t *driver, device_t parent)
       ? SYS_RES_IOPORT : SYS_RES_MEMORY;
     rstart = AcpiGbl_FADT->XPmTmrBlk.Address;
     bus_set_resource(dev, rtype, rid, rstart, rlen);
-    acpi_timer_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE);
+    acpi_timer_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE);
     if (acpi_timer_reg == NULL) {
        device_printf(dev, "couldn't allocate I/O resource (%s 0x%lx)\n",
                      rtype == SYS_RES_IOPORT ? "port" : "mem", rstart);
        return_VOID;
     }
+    acpi_timer_bsh = rman_get_bushandle(acpi_timer_reg);
+    acpi_timer_bst = rman_get_bustag(acpi_timer_reg);
+    if (AcpiGbl_FADT->TmrValExt != 0)
+       acpi_timer_timecounter.tc_counter_mask = 0xffffffff;
+    else
+       acpi_timer_timecounter.tc_counter_mask = 0x00ffffff;
+    acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
     if (testenv("debug.acpi.timer_test"))
-       acpi_timer_test();
+       acpi_timer_boot_test();
 
-    acpi_timer_timecounter.tc_frequency = acpi_timer_frequency;
+    /*
+     * If all tests of the counter succeed, use the ACPI-fast method.  If
+     * at least one failed, default to using the safe routine, which reads
+     * the timer multiple times to get a consistent value before returning.
+     */
     j = 0;
-    for(i = 0; i < 10; i++)
-       j += test_counter();
+    for (i = 0; i < 10; i++)
+       j += acpi_timer_test();
     if (j == 10) {
        acpi_timer_timecounter.tc_name = "ACPI-fast";
        acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
@@ -226,28 +196,31 @@ acpi_timer_attach(device_t dev)
 /*
  * Fetch current time value from reliable hardware.
  */
-static unsigned
+static u_int
 acpi_timer_get_timecount(struct timecounter *tc)
 {
-    return (read_counter());
+    return (acpi_timer_read());
 }
 
 /*
  * Fetch current time value from hardware that may not correctly
- * latch the counter.
+ * latch the counter.  We need to read until we have three monotonic
+ * samples and then use the middle one, otherwise we are not protected
+ * against the fact that the bits can be wrong in two directions.  If
+ * we only cared about monosity, two reads would be enough.
  */
-static unsigned
+static u_int
 acpi_timer_get_timecount_safe(struct timecounter *tc)
 {
-    unsigned u1, u2, u3;
+    u_int u1, u2, u3;
 
-    u2 = read_counter();
-    u3 = read_counter();
+    u2 = acpi_timer_read();
+    u3 = acpi_timer_read();
     do {
        u1 = u2;
        u2 = u3;
-       u3 = read_counter();
-    } while (u1 > u2 || u2 > u3 || u3 - u1 > 15);
+       u3 = acpi_timer_read();
+    } while (u1 > u2 || u2 > u3);
 
     return (u2);
 }
@@ -277,48 +250,13 @@ SYSCTL_PROC(_machdep, OID_AUTO, acpi_timer_freq, CTLTYPE_INT | CTLFLAG_RW,
            0, sizeof(u_int), acpi_timer_sysctl_freq, "I", "");
 
 /*
- * Test harness for verifying ACPI timer behaviour.
- * Boot with debug.acpi.timer_test set to invoke this.
- */
-static void
-acpi_timer_test(void)
-{
-    u_int32_t  u1, u2, u3;
-    
-    u1 = read_counter();
-    u2 = read_counter();
-    u3 = read_counter();
-    
-    device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
-    for (;;) {
-       /*
-        * The failure case is where u3 > u1, but u2 does not fall between
-        * the two, ie. it contains garbage.
-        */
-       if (u3 > u1) {
-           if (u2 < u1 || u2 > u3)
-               device_printf(acpi_timer_dev,
-                             "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
-                             u1, u2, u3);
-       }
-       u1 = u2;
-       u2 = u3;
-       u3 = read_counter();
-    }
-}
-
-/*
- * Chipset workaround driver hung off PCI.
- *
  * Some ACPI timers are known or believed to suffer from implementation
- * problems which can lead to erroneous values being read from the timer.
- *
- * Since we can't trust unknown chipsets, we default to a timer-read
- * routine which compensates for the most common problem (as detailed
- * in the excerpt from the Intel PIIX4 datasheet below).
+ * problems which can lead to erroneous values being read.  This function
+ * tests for consistent results from the timer and returns 1 if it believes
+ * the timer is consistent, otherwise it returns 0.
  *
- * When we detect a known-functional chipset, we disable the workaround
- * to improve speed.
+ * It appears the cause is that the counter is not latched to the PCI bus
+ * clock when read:
  *
  * ] 20. ACPI Timer Errata
  * ]
@@ -334,61 +272,77 @@ acpi_timer_test(void)
  * ] Workaround: Read the register twice and compare.
  * ] Status: This will not be fixed in the PIIX4 or PIIX4E, it is fixed
  * ] in the PIIX4M.
- *
- * The counter is in other words not latched to the PCI bus clock when
- * read.  Notice the workaround isn't:  We need to read until we have
- * three monotonic samples and then use the middle one, otherwise we are
- * not protected against the fact that the bits can be wrong in two
- * directions.  If we only cared about monosity two reads would be enough.
  */
+#define N 2000
+static int
+acpi_timer_test()
+{
+    uint32_t   last, this;
+    int                min, max, n, delta;
+    register_t s;
 
-#if 0
-static int     acpi_timer_pci_probe(device_t dev);
+    min = 10000000;
+    max = 0;
 
-static device_method_t acpi_timer_pci_methods[] = {
-    DEVMETHOD(device_probe,    acpi_timer_pci_probe),
-    {0, 0}
-};
+    /* Test the timer with interrupts disabled to get accurate results. */
+    s = intr_disable();
+    last = acpi_timer_read();
+    for (n = 0; n < N; n++) {
+       this = acpi_timer_read();
+       delta = acpi_TimerDelta(this, last);
+       if (delta > max)
+           max = delta;
+       else if (delta < min)
+           min = delta;
+       last = this;
+    }
+    intr_restore(s);
 
-static driver_t acpi_timer_pci_driver = {
-    "acpi_timer_pci",
-    acpi_timer_pci_methods,
-    0,
-};
+    if (max - min > 2)
+       n = 0;
+    else if (min < 0 || max == 0)
+       n = 0;
+    else
+       n = 1;
+    if (bootverbose) {
+       printf("ACPI timer looks %s min = %d, max = %d, width = %d\n",
+               n ? "GOOD" : "BAD ",
+               min, max, max - min);
+    }
 
-devclass_t acpi_timer_pci_devclass;
-DRIVER_MODULE(acpi_timer_pci, pci, acpi_timer_pci_driver,
-             acpi_timer_pci_devclass, 0, 0);
+    return (n);
+}
+#undef N
 
 /*
- * Look at PCI devices going past; if we detect one we know contains
- * a functional ACPI timer device, enable the faster timecounter read
- * routine.
+ * Test harness for verifying ACPI timer behaviour.
+ * Boot with debug.acpi.timer_test set to invoke this.
  */
-static int
-acpi_timer_pci_probe(device_t dev)
+static void
+acpi_timer_boot_test(void)
 {
-    int vendor, device, revid;
-    
-    vendor = pci_get_vendor(dev);
-    device = pci_get_device(dev);
-    revid  = pci_get_revid(dev);
-    
-    /* Detect the PIIX4M and i440MX, respectively */
-    if ((vendor == 0x8086 && device == 0x7113 && revid >= 0x03)        ||
-       (vendor == 0x8086 && device == 0x719b)) {
+    uint32_t u1, u2, u3;
 
-       acpi_timer_timecounter.tc_get_timecount = acpi_timer_get_timecount;
-       acpi_timer_timecounter.tc_name = "ACPI-fast";
-       if (bootverbose) {
-           device_printf(acpi_timer_dev,"functional ACPI timer detected, "
-                         "enabling fast timecount interface\n");
+    u1 = acpi_timer_read();
+    u2 = acpi_timer_read();
+    u3 = acpi_timer_read();
+
+    device_printf(acpi_timer_dev, "timer test in progress, reboot to quit.\n");
+    for (;;) {
+       /*
+        * The failure case is where u3 > u1, but u2 does not fall between
+        * the two, ie. it contains garbage.
+        */
+       if (u3 > u1) {
+           if (u2 < u1 || u2 > u3)
+               device_printf(acpi_timer_dev,
+                             "timer is not monotonic: 0x%08x,0x%08x,0x%08x\n",
+                             u1, u2, u3);
        }
+       u1 = u2;
+       u2 = u3;
+       u3 = acpi_timer_read();
     }
-
-    /* We never match anything */
-    return (ENXIO);
 }
-#endif
 
 #endif /* 0 */