Home | History | Annotate | Line # | Download | only in acpi
acpi_timer.c revision 1.15
      1 /* $NetBSD: acpi_timer.c,v 1.15 2010/03/05 14:00:17 jruoho Exp $ */
      2 
      3 #include <sys/cdefs.h>
      4 __KERNEL_RCSID(0, "$NetBSD: acpi_timer.c,v 1.15 2010/03/05 14:00:17 jruoho Exp $");
      5 
      6 #include <sys/types.h>
      7 #include <sys/systm.h>
      8 #include <sys/time.h>
      9 #include <sys/timetc.h>
     10 
     11 #include <dev/acpi/acpivar.h>
     12 #include <dev/acpi/acpi_timer.h>
     13 
     14 #include <machine/acpi_machdep.h>
     15 
     16 static int acpitimer_test(void);
     17 static uint32_t acpitimer_delta(uint32_t, uint32_t);
     18 static u_int acpitimer_read_safe(struct timecounter *);
     19 static u_int acpitimer_read_fast(struct timecounter *);
     20 
     21 static struct timecounter acpi_timecounter = {
     22 	acpitimer_read_safe,
     23 	0,
     24 	0x00ffffff,
     25 	PM_TIMER_FREQUENCY,
     26 	"ACPI-Safe",
     27 	900,
     28 	NULL,
     29 	NULL,
     30 };
     31 
     32 int
     33 acpitimer_init(void)
     34 {
     35 	uint32_t bits;
     36 	int i, j;
     37 	ACPI_STATUS res;
     38 
     39 	res = AcpiGetTimerResolution(&bits);
     40 	if (res != AE_OK)
     41 		return (-1);
     42 
     43 	if (bits == 32)
     44 		acpi_timecounter.tc_counter_mask = 0xffffffff;
     45 
     46 	j = 0;
     47 	for (i = 0; i < 10; i++)
     48 		j += acpitimer_test();
     49 
     50 	if (j >= 10) {
     51 		acpi_timecounter.tc_name = "ACPI-Fast";
     52 		acpi_timecounter.tc_get_timecount = acpitimer_read_fast;
     53 		acpi_timecounter.tc_quality = 1000;
     54 	}
     55 
     56 	tc_init(&acpi_timecounter);
     57 	aprint_verbose("%s %d-bit timer\n", acpi_timecounter.tc_name, bits);
     58 
     59 	return (0);
     60 }
     61 
     62 int
     63 acpitimer_detach(void)
     64 {
     65 	return tc_detach(&acpi_timecounter);
     66 }
     67 
     68 static u_int
     69 acpitimer_read_fast(struct timecounter *tc)
     70 {
     71 	uint32_t t;
     72 
     73 	AcpiGetTimer(&t);
     74 	return (t);
     75 }
     76 
     77 /*
     78  * Some chipsets (PIIX4 variants) do not latch correctly; there
     79  * is a chance that a transition is hit.
     80  */
     81 static u_int
     82 acpitimer_read_safe(struct timecounter *tc)
     83 {
     84 	uint32_t t1, t2, t3;
     85 
     86 	AcpiGetTimer(&t2);
     87 	AcpiGetTimer(&t3);
     88 	do {
     89 		t1 = t2;
     90 		t2 = t3;
     91 		AcpiGetTimer(&t3);
     92 	} while ((t1 > t2) || (t2 > t3));
     93 	return (t2);
     94 }
     95 
     96 static uint32_t
     97 acpitimer_delta(uint32_t end, uint32_t start)
     98 {
     99 	uint32_t delta;
    100 	u_int mask = acpi_timecounter.tc_counter_mask;
    101 
    102 	if (end >= start)
    103 		delta = end - start;
    104 	else
    105 		delta = ((mask - start) + end + 1) & mask;
    106 
    107 	return (delta);
    108 }
    109 
    110 #define N 2000
    111 static int
    112 acpitimer_test(void)
    113 {
    114 	uint32_t last, this, delta;
    115 	int minl, maxl, n;
    116 
    117 	minl = 10000000;
    118 	maxl = 0;
    119 
    120 	acpi_md_OsDisableInterrupt();
    121 	AcpiGetTimer(&last);
    122 	for (n = 0; n < N; n++) {
    123 		AcpiGetTimer(&this);
    124 		delta = acpitimer_delta(this, last);
    125 		if (delta > maxl)
    126 			maxl = delta;
    127 		else if (delta < minl)
    128 			minl = delta;
    129 		last = this;
    130 	}
    131 	acpi_md_OsEnableInterrupt();
    132 
    133 	if (maxl - minl > 2 )
    134 		n = 0;
    135 	else if (minl < 0 || maxl == 0)
    136 		n = 0;
    137 	else
    138 		n = 1;
    139 
    140 	return (n);
    141 }
    142