Allow one shot timer to be switched on a running system between i8254 and
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 3 May 2009 07:36:56 +0000 (15:36 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 3 May 2009 11:22:47 +0000 (19:22 +0800)
lapic timer:

- Always register "clk" interrupt.
- Add cputimer_intr_switch(), which could switch one shot timer between
  i8254 and lapic timer on a running system.  It could be used to select
  a proper one shot timer duing ACPI C3 transition:
  e.g. ->C3 use i8254, C3-> use lapic timer
- Add sysctl node hw.cputimer_intr_type to test cputimer_intr_switch().

sys/platform/pc32/apic/mpapic.c
sys/platform/pc32/isa/clock.c
sys/platform/pc64/isa/clock.c
sys/platform/vkernel/platform/systimer.c
sys/sys/systimer.h

index 5a1c6f6..c5273a1 100644 (file)
@@ -46,12 +46,14 @@ static void lapic_timer_calibrate(void);
 static void    lapic_timer_set_divisor(int);
 static void    lapic_timer_intr_reload(sysclock_t);
 static void    lapic_timer_fixup_handler(void *);
+static void    lapic_timer_restart_handler(void *);
 
 void           lapic_timer_fixup(void);
 void           lapic_timer_process(void);
 void           lapic_timer_process_frame(struct intrframe *);
 void           lapic_timer_intr_test(void);
 void           lapic_timer_oneshot_intr_enable(void);
+void           lapic_timer_restart(void);
 
 int            lapic_timer_test;
 int            lapic_timer_enable;
@@ -313,8 +315,13 @@ lapic_timer_oneshot_intr_enable(void)
 }
 
 static void
-lapic_timer_fixup_handler(void *dummy __unused)
+lapic_timer_fixup_handler(void *arg)
 {
+       int *started = arg;
+
+       if (started != NULL)
+               *started = 0;
+
        if (strcmp(cpu_vendor, "AuthenticAMD") == 0) {
                /*
                 * Detect the presence of C1E capability mostly on latest
@@ -346,11 +353,28 @@ lapic_timer_fixup_handler(void *dummy __unused)
                                 */
                                gd->gd_timer_running = 1;
                                lapic_timer_oneshot_quick(2);
+
+                               if (started != NULL)
+                                       *started = 1;
                        }
                }
        }
 }
 
+static void
+lapic_timer_restart_handler(void *dummy __unused)
+{
+       int started;
+
+       lapic_timer_fixup_handler(&started);
+       if (!started) {
+               struct globaldata *gd = mycpu;
+
+               gd->gd_timer_running = 1;
+               lapic_timer_oneshot_quick(2);
+       }
+}
+
 /*
  * This function is called only by ACPI-CA code currently:
  * - AMD C1E fixup.  AMD C1E only seems to happen after ACPI
@@ -366,6 +390,14 @@ lapic_timer_fixup(void)
        }
 }
 
+void
+lapic_timer_restart(void)
+{
+       KKASSERT(lapic_timer_enable);
+       cputimer_intr_reload = lapic_timer_intr_reload;
+       lwkt_send_ipiq_mask(smp_active_mask, lapic_timer_restart_handler, NULL);
+}
+
 
 /*
  * dump contents of local APIC registers
index ed6eb88..ab95867 100644 (file)
@@ -370,6 +370,7 @@ extern int  lapic_timer_test;
 extern int     lapic_timer_enable;
 extern void    lapic_timer_oneshot_intr_enable(void);
 extern void    lapic_timer_intr_test(void);
+extern void    lapic_timer_restart(void);
 
 /* Piggyback lapic_timer test */
 static void
@@ -394,6 +395,52 @@ cputimer_intr_enable(void)
 #endif
 }
 
+void
+cputimer_intr_switch(enum cputimer_intr_type type)
+{
+#ifdef SMP
+       if (lapic_timer_enable || lapic_timer_test) {
+               switch (type) {
+               case CPUTIMER_INTRT_C3:
+                       cputimer_intr_reload = i8254_intr_reload;
+                       /* Force a quick reload */
+                       i8254_intr_reload(0);
+                       break;
+
+               case CPUTIMER_INTRT_FAST:
+                       if (lapic_timer_test) /* XXX */
+                               cputimer_intr_reload = i8254_intr_reload_test;
+                       else if (lapic_timer_enable)
+                               lapic_timer_restart();
+                       break;
+               }
+       }
+#endif
+}
+
+static int
+sysctl_cputimer_intr_switch(SYSCTL_HANDLER_ARGS)
+{
+       enum cputimer_intr_type type = CPUTIMER_INTRT_FAST;
+       int error;
+
+       error = sysctl_handle_int(oidp, &type, 0, req);
+       if (error || req->newptr == NULL)
+               return error;
+       switch (type) {
+       case CPUTIMER_INTRT_C3:
+       case CPUTIMER_INTRT_FAST:
+               break;
+       default:
+               return EINVAL;
+       }
+       cputimer_intr_switch(type);
+       return 0;
+}
+SYSCTL_PROC(_hw, OID_AUTO, cputimer_intr_type, CTLTYPE_INT | CTLFLAG_RW,
+           0, 0, sysctl_cputimer_intr_switch, "I",
+           "cputimer_intr switch [0|1]");
+
 /*
  * DELAY(usec)      - Spin for the specified number of microseconds.
  * DRIVERSLEEP(usec) - Spin for the specified number of microseconds,
@@ -1020,11 +1067,6 @@ cpu_initclocks(void *arg __unused)
 
        callout_init(&sysbeepstop_ch);
 
-#ifdef SMP
-       if (lapic_timer_enable)
-               return;
-#endif
-
        if (statclock_disable) {
                /*
                 * The stat interrupt mask is different without the
index d5aefa4..ba5a449 100644 (file)
@@ -371,6 +371,11 @@ cputimer_intr_enable(void)
 {
 }
 
+void
+cputimer_intr_switch(enum cputimer_intr_type type)
+{
+}
+
 /*
  * DELAY(usec)      - Spin for the specified number of microseconds.
  * DRIVERSLEEP(usec) - Spin for the specified number of microseconds,
index 30c3a93..9a89f00 100644 (file)
@@ -131,6 +131,11 @@ cputimer_intr_enable(void)
 {
 }
 
+void
+cputimer_intr_switch(enum cputimer_intr_type type)
+{
+}
+
 /*
  * Get the current counter, with 2's complement rollover.
  *
index 81668be..dd0d3ae 100644 (file)
@@ -124,6 +124,11 @@ extern struct cputimer *sys_cputimer;
 #define CPUTIMER_PRI_HPET      15
 #define CPUTIMER_PRI_VKERNEL   20
 
+enum cputimer_intr_type {
+       CPUTIMER_INTRT_C3 = 0,
+       CPUTIMER_INTRT_FAST
+};
+
 /*
  * note that cputimer_count() always returns a full-width wrapping counter.
  */
@@ -139,6 +144,7 @@ void cputimer_default_destruct(struct cputimer *);
 void cputimer_intr_enable(void);
 void cputimer_intr_config(struct cputimer *);
 extern void (*cputimer_intr_reload)(sysclock_t);
+void cputimer_intr_switch(enum cputimer_intr_type);
 
 #endif