Use the STICK logic on UltraSPARC-IIe to generate clock interrupts.
authorkettenis <kettenis@openbsd.org>
Sun, 10 Aug 2008 14:13:05 +0000 (14:13 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 10 Aug 2008 14:13:05 +0000 (14:13 +0000)
sys/arch/sparc64/include/cpu.h
sys/arch/sparc64/include/ctlreg.h
sys/arch/sparc64/sparc64/clock.c
sys/arch/sparc64/sparc64/locore.s

index de4057b..4fa56d7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: cpu.h,v 1.65 2008/08/07 21:25:48 kettenis Exp $       */
+/*     $OpenBSD: cpu.h,v 1.66 2008/08/10 14:13:05 kettenis Exp $       */
 /*     $NetBSD: cpu.h,v 1.28 2001/06/14 22:56:58 thorpej Exp $ */
 
 /*
@@ -250,12 +250,8 @@ void       dumpconf(void);
 caddr_t        reserve_dumppages(caddr_t);
 /* clock.c */
 struct timeval;
-int    tickintr(void *); /* level 10 (tick) interrupt code */
-int    sys_tickintr(void *); /* level 10 (sys_tick) interrupt code */
 int    clockintr(void *);/* level 10 (clock) interrupt code */
 int    statintr(void *);       /* level 14 (statclock) interrupt code */
-void   tick_start(void);
-void   sys_tick_start(void);
 /* locore.s */
 struct fpstate64;
 void   savefpstate(struct fpstate64 *);
index 68b13fc..0a9cc50 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ctlreg.h,v 1.20 2008/08/07 21:25:48 kettenis Exp $    */
+/*     $OpenBSD: ctlreg.h,v 1.21 2008/08/10 14:13:05 kettenis Exp $    */
 /*     $NetBSD: ctlreg.h,v 1.28 2001/08/06 23:55:34 eeh Exp $ */
 
 /*
@@ -728,9 +728,11 @@ void flush(void *p)
 /* Read 64-bit %tick and %sys_tick registers. */
 #define tick() (sparc_rdpr(tick) & TICK_TICKS)
 #define sys_tick() (sparc_rd(sys_tick) & TICK_TICKS)
+extern u_int64_t stick(void);
 
 extern void tickcmpr_set(u_int64_t);
 extern void sys_tickcmpr_set(u_int64_t);
+extern void stickcmpr_set(u_int64_t);
 
 #endif /* _LOCORE */
 #endif /* _SPARC64_CTLREG_ */
index 329cf66..8dc5674 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: clock.c,v 1.46 2008/08/07 21:25:47 kettenis Exp $     */
+/*     $OpenBSD: clock.c,v 1.47 2008/08/10 14:13:05 kettenis Exp $     */
 /*     $NetBSD: clock.c,v 1.41 2001/07/24 19:29:25 eeh Exp $ */
 
 /*
@@ -132,7 +132,15 @@ int statvar = 8192;
 int statmin;                   /* statclock interval - 1/2*variance */
 
 static long tick_increment;
-int schedintr(void *);
+
+void   tick_start(void);
+void   sys_tick_start(void);
+void   stick_start(void);
+
+int    tickintr(void *);
+int    sys_tickintr(void *);
+int    stickintr(void *);
+int    schedintr(void *);
 
 static struct intrhand level10 = { clockintr };
 static struct intrhand level0 = { tickintr };
@@ -615,10 +623,15 @@ cpu_initclocks(void)
                /* We only have one timer so we have no statclock */
                stathz = 0;     
 
-               if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) {
+               if (sys_tick_rate > 0) {
                        tick_increment = sys_tick_rate / hz;
-                       level0.ih_fun = sys_tickintr;
-                       cpu_start_clock = sys_tick_start;
+                       if (impl == IMPL_HUMMINGBIRD) {
+                               level0.ih_fun = stickintr;
+                               cpu_start_clock = stick_start;
+                       } else {
+                               level0.ih_fun = sys_tickintr;
+                               cpu_start_clock = sys_tick_start;
+                       }
                } else {
                        /* set the next interrupt time */
                        tick_increment = cpu_clockrate / hz;
@@ -799,6 +812,30 @@ sys_tickintr(cap)
        return (1);
 }
 
+int
+stickintr(cap)
+       void *cap;
+{
+       struct cpu_info *ci = curcpu();
+       u_int64_t s;
+
+       /*
+        * Do we need to worry about overflow here?
+        */
+       while (ci->ci_tick < stick()) {
+               ci->ci_tick += tick_increment;
+               hardclock((struct clockframe *)cap);
+               level0.ih_count.ec_count++;
+       }
+
+       /* Reset the interrupt. */
+       s = intr_disable();
+       stickcmpr_set(ci->ci_tick);
+       intr_restore(s);
+
+       return (1);
+}
+
 /*
  * Level 14 (stat clock) interrupt handler.
  */
@@ -974,6 +1011,23 @@ sys_tick_start(void)
        intr_restore(s);
 }
 
+void
+stick_start(void)
+{
+       struct cpu_info *ci = curcpu();
+       u_int64_t s;
+
+       /*
+        * Try to make the tick interrupts as synchronously as possible on
+        * all CPUs to avoid inaccuracies for migrating processes.
+        */
+
+       s = intr_disable();
+       ci->ci_tick = roundup(stick(), tick_increment);
+       stickcmpr_set(ci->ci_tick);
+       intr_restore(s);
+}
+
 u_int
 tick_get_timecount(struct timecounter *tc)
 {
index f996fa7..4b1a0d4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: locore.s,v 1.151 2008/08/07 21:25:47 kettenis Exp $   */
+/*     $OpenBSD: locore.s,v 1.152 2008/08/10 14:13:05 kettenis Exp $   */
 /*     $NetBSD: locore.s,v 1.137 2001/08/13 06:10:10 jdolecek Exp $    */
 
 /*
@@ -8939,6 +8939,58 @@ ENTRY(sys_tickcmpr_set)
        retl
         nop
 
+/*
+ * Support for the STICK logic found on the integrated PCI host bridge
+ * of Hummingbird (UltraSPARC-IIe).  The chip designers made the
+ * brilliant decision to split the 64-bit counters into two 64-bit
+ * aligned 32-bit registers, making atomic access impossible.  This
+ * means we have to check for wraparound in various places.  Sigh.
+ */
+
+#define STICK_CMP_LOW  0x1fe0000f060
+#define STICK_CMP_HIGH 0x1fe0000f068
+#define STICK_REG_LOW  0x1fe0000f070
+#define STICK_REG_HIGH 0x1fe0000f078
+
+ENTRY(stick)
+       setx    STICK_REG_LOW, %o1, %o3
+0:
+       ldxa    [%o3] ASI_PHYS_NON_CACHED, %o0
+       add     %o3, (STICK_REG_HIGH - STICK_REG_LOW), %o4
+       ldxa    [%o4] ASI_PHYS_NON_CACHED, %o1
+       ldxa    [%o3] ASI_PHYS_NON_CACHED, %o2
+       cmp     %o2, %o0                ! Check for wraparound
+       blu,pn  %icc, 0b
+        sllx   %o1, 33, %o1            ! Clear the MSB
+       srlx    %o1, 1, %o1
+       retl
+        or     %o2, %o1, %o0
+
+ENTRY(stickcmpr_set)
+       setx    STICK_CMP_HIGH, %o1, %o3
+       mov     8, %o2                  ! Initial step size
+1:
+       srlx    %o0, 32, %o1
+       stxa    %o1, [%o3] ASI_PHYS_NON_CACHED
+       add     %o3, (STICK_CMP_LOW - STICK_CMP_HIGH), %o4
+       stxa    %o0, [%o4] ASI_PHYS_NON_CACHED
+
+       add     %o3, (STICK_REG_LOW - STICK_CMP_HIGH), %o4
+       ldxa    [%o4] ASI_PHYS_NON_CACHED, %o1
+       add     %o3, (STICK_REG_HIGH - STICK_CMP_HIGH), %o4
+       ldxa    [%o4] ASI_PHYS_NON_CACHED, %o5
+       sllx    %o5, 32, %o5
+       or      %o1, %o5, %o1
+
+       cmp     %o0, %o1                ! Make sure the value we wrote
+       bg,pt   %xcc, 2f                !   was in the future
+        add    %o0, %o2, %o0           ! If not, add the step size, double
+       ba,pt   %xcc, 1b                !   the step size and try again.
+        sllx   %o2, 1, %o2
+2:
+       retl
+        nop
+
 #define MICROPERSEC    (1000000)
        .data
        .align  16