lapic timer: Add lapic timer calibration code.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 1 May 2009 07:00:31 +0000 (15:00 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 1 May 2009 10:52:29 +0000 (18:52 +0800)
The calibrated information is not used yet.

Obtained-from: FreeBSD (jhb@freebsd.org)

sys/platform/pc32/apic/mpapic.c
sys/platform/pc32/i386/mp_machdep.c
sys/platform/pc32/include/smp.h

index 4376d3f..771e39c 100644 (file)
@@ -40,6 +40,8 @@
 #define ELCR0  0x4d0                   /* eisa irq 0-7 */
 #define ELCR1  0x4d1                   /* eisa irq 8-15 */
 
+static void    lapic_timer_calibrate(void);
+
 /*
  * pointers to pmapped apic hardware.
  */
@@ -50,7 +52,7 @@ volatile ioapic_t     **ioapic;
  * Enable APIC, configure interrupts.
  */
 void
-apic_initialize(void)
+apic_initialize(boolean_t bsp)
 {
        u_int   temp;
 
@@ -135,11 +137,65 @@ apic_initialize(void)
        lapic.eoi = 0;
        lapic.eoi = 0;
 
+       if (bsp)
+               lapic_timer_calibrate();
+
        if (bootverbose)
                apic_dump("apic_initialize()");
 }
 
 
+static int             lapic_timer_divisor_idx = -1;
+static const uint32_t  lapic_timer_divisors[] = {
+       APIC_TDCR_2,    APIC_TDCR_4,    APIC_TDCR_8,    APIC_TDCR_16,
+       APIC_TDCR_32,   APIC_TDCR_64,   APIC_TDCR_128,  APIC_TDCR_1
+};
+#define APIC_TIMER_NDIVISORS \
+       (int)(sizeof(lapic_timer_divisors) / sizeof(lapic_timer_divisors[0]))
+
+static void
+lapic_timer_set_divisor(int divisor_idx)
+{
+       KKASSERT(divisor_idx >= 0 && divisor_idx < APIC_TIMER_NDIVISORS);
+       lapic.dcr_timer = lapic_timer_divisors[divisor_idx];
+}
+
+static void
+lapic_timer_oneshot(u_int count)
+{
+       uint32_t value;
+
+       value = lapic.lvt_timer;
+       value &= ~APIC_LVTT_PERIODIC;
+       lapic.lvt_timer = value;
+       lapic.icr_timer = count;
+}
+
+static void
+lapic_timer_calibrate(void)
+{
+       u_long value;
+
+       /* Try to calibrate the local APIC timer. */
+       for (lapic_timer_divisor_idx = 0;
+            lapic_timer_divisor_idx < APIC_TIMER_NDIVISORS;
+            lapic_timer_divisor_idx++) {
+               lapic_timer_set_divisor(lapic_timer_divisor_idx);
+               lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
+               DELAY(2000000);
+               value = APIC_TIMER_MAX_COUNT - lapic.ccr_timer;
+               if (value != APIC_TIMER_MAX_COUNT)
+                       break;
+       }
+       if (lapic_timer_divisor_idx >= APIC_TIMER_NDIVISORS)
+               panic("lapic: no proper timer divisor?!\n");
+       value /= 2;
+
+       kprintf("lapic: divisor index %d, frequency %lu hz\n",
+               lapic_timer_divisor_idx, value);
+}
+
+
 /*
  * dump contents of local APIC registers
  */
index fae58dc..023eedb 100644 (file)
@@ -2043,8 +2043,8 @@ start_all_aps(u_int boot_addr)
 
        POSTCODE(START_ALL_APS_POST);
 
-       /* initialize BSP's local APIC */
-       apic_initialize();
+       /* Initialize BSP's local APIC */
+       apic_initialize(TRUE);
        bsp_apic_ready = 1;
 
        /* install the AP 1st level boot code */
@@ -2487,8 +2487,8 @@ ap_init(void)
                panic("cpuid mismatch! boom!!");
        }
 
-       /* Init local apic for irq's */
-       apic_initialize();
+       /* Initialize AP's local APIC for irq's */
+       apic_initialize(FALSE);
 
        /* Set memory range attributes for this CPU to match the BSP */
        mem_range_AP_init();
index 7af4292..0d6fc5a 100644 (file)
@@ -112,7 +112,7 @@ extern volatile ioapic_t    **ioapic;
 
 /* functions in mpapic.c */
 void   apic_dump               (char*);
-void   apic_initialize         (void);
+void   apic_initialize         (boolean_t);
 void   imen_dump               (void);
 int    apic_ipi                (int, int, int);
 void   selected_apic_ipi       (u_int, int, int);