Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Mar 2009 12:55:11 +0000 (UTC)
From:      Marius Strobl <marius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r190033 - in stable/7/sys: . contrib/pf dev/ath/ath_hal dev/cxgb sparc64/include sparc64/sparc64
Message-ID:  <200903191255.n2JCtB7p061565@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marius
Date: Thu Mar 19 12:55:11 2009
New Revision: 190033
URL: http://svn.freebsd.org/changeset/base/190033

Log:
  MFC: r182730, r182743, r183201
  
  - USIII-based machines can consist of CPUs running at different
    frequencies (and having different cache sizes) so use the STICK
    (System TICK) timer, which was introduced due to this and is
    driven by the same frequency across all CPUs, instead of the
    TICK timer, whose frequency varies with the CPU clock, to drive
    hardclock. We use the STICK timers only when absolutely necessary
    though as the STICK timers are driven at frequencies as low as
    5MHz, resulting in bad granularity compared to the TICK timers.
    However, don't employ the workaround for the BlackBird erratum #1
    when using the TICK timer on machines with cheetah-class CPUs for
    performance reasons.
    Note that using the STICK counter also causes a hang with USIIIi
    MP machines for reasons unknown, but these can only consist of
    identical CPUs anyway.
  - Given that we only (try to) synchronize the (S)TICK timers of APs
    with the BSP during startup, we could end up spinning forever in
    DELAY(9) if that function is migrated to another CPU while we're
    spinning due to clock drift afterwards, so pin to the CPU in order
    to avoid migration. Unfortunately, pinning doesn't work at the
    point DELAY(9) is required by the low-level console drivers, yet,
    so switch to a function pointer, which is updated accordingly, for
    implementing DELAY(9). For USIII and beyond, this would also allow
    to easily use the STICK counter instead of the TICK one here,
    there's no benefit in doing so however.
    While at it, use cpu_spinwait(9) for spinning in the delay-
    functions. This currently is a NOP though.
  - Don't set the TICK timer of the BSP to 0 during startup as there's
    no need to do so.
  - Implement cpu_est_clockrate().
  - Unfortunately, USIIIi-based machines don't provide a timecounter
    device besides the STICK and TICK counters (well, in theory the
    Tomatillo bridges have a performance counter that can be (ab)used
    as timecounter by configuring it to count bus cycles, though unlike
    the performance counter of Schizo bridges, the Tomatillo one is
    broken and counts Sun knows what in this mode). This means that
    we've to use a (S)TICK counter for timecounting, which has the old
    problem of not being in sync across CPUs, so provide an additional
    timecounter function which binds itself to the BSP but has an
    adequate low priority.

Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/ath/ath_hal/   (props changed)
  stable/7/sys/dev/cxgb/   (props changed)
  stable/7/sys/sparc64/include/clock.h
  stable/7/sys/sparc64/include/cpufunc.h
  stable/7/sys/sparc64/include/pcpu.h
  stable/7/sys/sparc64/include/smp.h
  stable/7/sys/sparc64/include/tick.h
  stable/7/sys/sparc64/include/ver.h
  stable/7/sys/sparc64/sparc64/clock.c
  stable/7/sys/sparc64/sparc64/exception.S
  stable/7/sys/sparc64/sparc64/genassym.c
  stable/7/sys/sparc64/sparc64/locore.S
  stable/7/sys/sparc64/sparc64/machdep.c
  stable/7/sys/sparc64/sparc64/mp_locore.S
  stable/7/sys/sparc64/sparc64/mp_machdep.c
  stable/7/sys/sparc64/sparc64/tick.c

Modified: stable/7/sys/sparc64/include/clock.h
==============================================================================
--- stable/7/sys/sparc64/include/clock.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/clock.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -29,10 +29,12 @@
 #ifndef	_MACHINE_CLOCK_H_
 #define	_MACHINE_CLOCK_H_
 
-extern u_long tick_increment;
-extern u_long tick_freq;
-extern u_long tick_MHz;
+extern void (*delay_func)(int usec);
+extern u_long clock_boot;
 
-int	sysbeep(int, int);
+void delay_boot(int usec);
+void delay_tick(int usec);
+
+int	sysbeep(int pitch, int period);
 
 #endif /* !_MACHINE_CLOCK_H_ */

Modified: stable/7/sys/sparc64/include/cpufunc.h
==============================================================================
--- stable/7/sys/sparc64/include/cpufunc.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/cpufunc.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -174,6 +174,15 @@ int fasword32(u_long asi, void *addr, ui
 } while (0)
 
 /*
+ * Trick GAS/GCC into compiling access to STICK/STICK_COMPARE independently
+ * of the selected instruction set.
+ */
+#define	rdstick()		rd(asr24)
+#define	rdstickcmpr()		rd(asr25)
+#define	wrstick(val, xor)	wr(asr24, (val), (xor))
+#define	wrstickcmpr(val, xor)	wr(asr25, (val), (xor))
+
+/*
  * Macro intended to be used instead of wr(asr23, val, xor) for writing to
  * the TICK_COMPARE register in order to avoid a bug in BlackBird CPUs that
  * can cause these writes to fail under certain condidtions which in turn

Modified: stable/7/sys/sparc64/include/pcpu.h
==============================================================================
--- stable/7/sys/sparc64/include/pcpu.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/pcpu.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -53,6 +53,7 @@ struct pmap;
 	vm_offset_t pc_addr;						\
 	u_long	pc_tickref;						\
 	u_long	pc_tickadj;						\
+	u_int	pc_clock;						\
 	u_int	pc_mid;							\
 	u_int	pc_node;						\
 	u_int	pc_tlb_ctx;						\

Modified: stable/7/sys/sparc64/include/smp.h
==============================================================================
--- stable/7/sys/sparc64/include/smp.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/smp.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -29,9 +29,10 @@
 #ifndef	_MACHINE_SMP_H_
 #define	_MACHINE_SMP_H_
 
-#define	CPU_CLKSYNC		1
-#define	CPU_INIT		2
-#define	CPU_BOOTSTRAP		3
+#define	CPU_TICKSYNC		1
+#define	CPU_STICKSYNC		2
+#define	CPU_INIT		3
+#define	CPU_BOOTSTRAP		4
 
 #ifndef	LOCORE
 
@@ -62,6 +63,7 @@ struct cpu_start_args {
 	u_int	csa_state;
 	vm_offset_t csa_pcpu;
 	u_long	csa_tick;
+	u_long	csa_stick;
 	u_long	csa_ver;
 	struct	tte csa_ttes[PCPU_PAGES];
 };

Modified: stable/7/sys/sparc64/include/tick.h
==============================================================================
--- stable/7/sys/sparc64/include/tick.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/tick.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -29,7 +29,9 @@
 #ifndef _MACHINE_TICK_H_
 #define	_MACHINE_TICK_H_
 
-void	tick_init(u_long clock);
+extern u_int hardclock_use_stick;
+
+void	tick_clear(void);
 void	tick_start(void);
 void	tick_stop(void);
 

Modified: stable/7/sys/sparc64/include/ver.h
==============================================================================
--- stable/7/sys/sparc64/include/ver.h	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/include/ver.h	Thu Mar 19 12:55:11 2009	(r190033)
@@ -41,6 +41,8 @@
 #define	VER_MAXTL_SIZE		(8)
 #define	VER_MAXWIN_SIZE		(5)
 
+#ifndef LOCORE
+
 #define	VER_MANUF_MASK		(((1L<<VER_MANUF_SIZE)-1)<<VER_MANUF_SHIFT)
 #define	VER_IMPL_MASK		(((1L<<VER_IMPL_SIZE)-1)<<VER_IMPL_SHIFT)
 #define	VER_MASK_MASK		(((1L<<VER_MASK_SIZE)-1)<<VER_MASK_SHIFT)
@@ -61,6 +63,8 @@
 extern int cpu_impl;
 extern char sparc64_model[];
 
+#endif /* !LOCORE */
+
 /* Known implementations. */
 #define	CPU_IMPL_SPARC64		0x01
 #define	CPU_IMPL_ULTRASPARCI		0x10

Modified: stable/7/sys/sparc64/sparc64/clock.c
==============================================================================
--- stable/7/sys/sparc64/sparc64/clock.c	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/clock.c	Thu Mar 19 12:55:11 2009	(r190033)
@@ -29,23 +29,56 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+
 #include <machine/clock.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+
+void (*delay_func)(int usec);
+u_long clock_boot;
+
+void
+DELAY(int usec)
+{
 
-u_long tick_increment;
-u_long tick_freq;
-u_long tick_MHz;
+	(*delay_func)(usec);
+}
 
 void
-DELAY(int n)
+delay_boot(int usec)
 {
-	u_long start, end;
+	u_long end;
 
-	start = rd(tick);
-	if (n < 0)
+	if (usec < 0)
 		return;
-	end = start + (u_long)n * tick_MHz;
+
+	end = rd(tick) + (u_long)usec * clock_boot / 1000000;
 	while (rd(tick) < end)
-		;
+		cpu_spinwait();
+}
+
+void
+delay_tick(int usec)
+{
+	u_long end;
+
+	if (usec < 0)
+		return;
+
+	/*
+	 * We avoid being migrated to another CPU with a possibly
+	 * unsynchronized TICK timer while spinning.
+	 */
+	sched_pin();
+
+	end = rd(tick) + (u_long)usec * PCPU_GET(clock) / 1000000;
+	while (rd(tick) < end)
+		cpu_spinwait();
+
+	sched_unpin();
 }
 
 void

Modified: stable/7/sys/sparc64/sparc64/exception.S
==============================================================================
--- stable/7/sys/sparc64/sparc64/exception.S	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/exception.S	Thu Mar 19 12:55:11 2009	(r190033)
@@ -550,7 +550,7 @@ END(tl0_sfsr_trap)
 	tl ## traplvl ## _intr	level, 1 << level
 
 #define	TICK(traplvl) \
-	tl ## traplvl ## _intr	PIL_TICK, 1
+	tl ## traplvl ## _intr	PIL_TICK, 0x10001
 
 #define	INTR_LEVEL(tl)							\
 	INTR(1, tl) ;							\

Modified: stable/7/sys/sparc64/sparc64/genassym.c
==============================================================================
--- stable/7/sys/sparc64/sparc64/genassym.c	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/genassym.c	Thu Mar 19 12:55:11 2009	(r190033)
@@ -83,10 +83,11 @@ ASSYM(PAGE_SIZE_4M, PAGE_SIZE_4M);
 ASSYM(CSA_PCPU, offsetof(struct cpu_start_args, csa_pcpu));
 ASSYM(CSA_STATE, offsetof(struct cpu_start_args, csa_state));
 #ifdef SUN4U
-ASSYM(CSA_TICK, offsetof(struct cpu_start_args, csa_tick));
-ASSYM(CSA_VER, offsetof(struct cpu_start_args, csa_ver));
 ASSYM(CSA_MID, offsetof(struct cpu_start_args, csa_mid));
+ASSYM(CSA_STICK, offsetof(struct cpu_start_args, csa_stick));
+ASSYM(CSA_TICK, offsetof(struct cpu_start_args, csa_tick));
 ASSYM(CSA_TTES, offsetof(struct cpu_start_args, csa_ttes));
+ASSYM(CSA_VER, offsetof(struct cpu_start_args, csa_ver));
 #endif
 #ifdef SUN4V
 ASSYM(CSA_CPUID, offsetof(struct cpu_start_args, csa_cpuid));

Modified: stable/7/sys/sparc64/sparc64/locore.S
==============================================================================
--- stable/7/sys/sparc64/sparc64/locore.S	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/locore.S	Thu Mar 19 12:55:11 2009	(r190033)
@@ -54,7 +54,6 @@ ENTRY(_start)
 	wrpr	%g0, 0, %cleanwin
 	wrpr	%g0, 0, %pil
 	wr	%g0, 0, %fprs
-	wrpr	%g0, 0, %tick
 
 	/*
 	 * Get onto our per-CPU panic stack, which precedes the struct pcpu in

Modified: stable/7/sys/sparc64/sparc64/machdep.c
==============================================================================
--- stable/7/sys/sparc64/sparc64/machdep.c	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/machdep.c	Thu Mar 19 12:55:11 2009	(r190033)
@@ -133,18 +133,6 @@ struct kva_md_info kmi;
 u_long ofw_vec;
 u_long ofw_tba;
 
-/*
- * Note: timer quality for CPU's is set low to try and prevent them from
- * being chosen as the primary timecounter.  The CPU counters are not
- * synchronized among the CPU's so in MP machines this causes problems
- * when calculating the time.  With this value the CPU's should only be
- * chosen as the primary timecounter as a last resort.
- */
-
-#define	UP_TICK_QUALITY	1000
-#define	MP_TICK_QUALITY	-100
-static struct timecounter tick_tc;
-
 char sparc64_model[32];
 
 static int cpu_use_vis = 1;
@@ -152,7 +140,6 @@ static int cpu_use_vis = 1;
 cpu_block_copy_t *cpu_block_copy;
 cpu_block_zero_t *cpu_block_zero;
 
-static timecounter_get_t tick_get_timecount;
 void sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3,
     ofw_vec_t *vec);
 void sparc64_shutdown_final(void *dummy, int howto);
@@ -180,22 +167,6 @@ cpu_startup(void *arg)
 	vm_paddr_t physsz;
 	int i;
 
-	tick_tc.tc_get_timecount = tick_get_timecount;
-	tick_tc.tc_poll_pps = NULL;
-	tick_tc.tc_counter_mask = ~0u;
-	tick_tc.tc_frequency = tick_freq;
-	tick_tc.tc_name = "tick";
-	tick_tc.tc_quality = UP_TICK_QUALITY;
-#ifdef SMP
-	/*
-	 * We do not know if each CPU's tick counter is synchronized.
-	 */
-	if (cpu_mp_probe())
-		tick_tc.tc_quality = MP_TICK_QUALITY;
-#endif
-
-	tc_init(&tick_tc);
-
 	physsz = 0;
 	for (i = 0; i < sparc64_nmemreg; i++)
 		physsz += sparc64_memreg[i].mr_size;
@@ -217,7 +188,7 @@ cpu_startup(void *arg)
 	if (bootverbose)
 		printf("machine: %s\n", sparc64_model);
 
-	cpu_identify(rdpr(ver), tick_freq, PCPU_GET(cpuid));
+	cpu_identify(rdpr(ver), PCPU_GET(clock), curcpu);
 }
 
 void
@@ -262,12 +233,6 @@ spinlock_exit(void)
 		wrpr(pil, td->td_md.md_saved_pil, 0);
 }
 
-unsigned
-tick_get_timecount(struct timecounter *tc)
-{
-	return ((unsigned)rd(tick));
-}
-
 void
 sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec)
 {
@@ -278,7 +243,6 @@ sparc64_init(caddr_t mdp, u_long o1, u_l
 	caddr_t kmdp;
 	phandle_t child;
 	phandle_t root;
-	u_int clock;
 	uint32_t portid;
 
 	end = 0;
@@ -291,6 +255,21 @@ sparc64_init(caddr_t mdp, u_long o1, u_l
 	cpu_impl = VER_IMPL(rdpr(ver));
 
 	/*
+	 * Clear (S)TICK timer (including NPT).
+	 */
+	tick_clear();
+
+	/*
+	 * UltraSparc II[e,i] based systems come up with the tick interrupt
+	 * enabled and a handler that resets the tick counter, causing DELAY()
+	 * to not work properly when used early in boot.
+	 * UltraSPARC III based systems come up with the system tick interrupt
+	 * enabled, causing an interrupt storm on startup since they are not
+	 * handled.
+	 */
+	tick_stop();
+
+	/*
 	 * Initialize Open Firmware (needed for console).
 	 */
 	OF_init(vec);
@@ -329,7 +308,7 @@ sparc64_init(caddr_t mdp, u_long o1, u_l
 	pc->pc_tlb_ctx_max = TLB_CTX_USER_MAX;
 
 	/*
-	 * Determine the OFW node (and ensure the
+	 * Determine the OFW node and frequency of the BSP (and ensure the
 	 * BSP is in the device tree in the first place).
 	 */
 	pc->pc_node = 0;
@@ -349,17 +328,23 @@ sparc64_init(caddr_t mdp, u_long o1, u_l
 	}
 	if (pc->pc_node == 0)
 		OF_exit();
+	if (OF_getprop(child, "clock-frequency", &pc->pc_clock,
+	    sizeof(pc->pc_clock)) <= 0)
+		OF_exit();
 
 	/*
-	 * Initialize the tick counter.  Must be before the console is inited
-	 * in order to provide the low-level console drivers with a working
-	 * DELAY().
+	 * Provide a DELAY() that works before PCPU_REG is set.  We can't
+	 * set PCPU_REG without also taking over the trap table or the
+	 * firmware will overwrite it.  Unfortunately, it's way to early
+	 * to also take over the trap table at this point.
 	 */
-	OF_getprop(child, "clock-frequency", &clock, sizeof(clock));
-	tick_init(clock);
+	clock_boot = pc->pc_clock;
+	delay_func = delay_boot;
 
 	/*
 	 * Initialize the console before printing anything.
+	 * NB: the low-level console drivers require a working DELAY() at
+	 * this point.
 	 */
 	cninit();
 
@@ -445,6 +430,11 @@ sparc64_init(caddr_t mdp, u_long o1, u_l
 	cpu_setregs(pc);
 
 	/*
+	 * It's now safe to use the real DELAY().
+	 */
+	delay_func = delay_tick;
+
+	/*
 	 * Initialize the message buffer (after setting trap table).
 	 */
 	msgbufinit(msgbufp, MSGBUF_SIZE);
@@ -720,8 +710,13 @@ cpu_shutdown(void *args)
 int
 cpu_est_clockrate(int cpu_id, uint64_t *rate)
 {
+	struct pcpu *pc;
 
-	return (ENXIO);
+	pc = pcpu_find(cpu_id);
+	if (pc == NULL || rate == NULL)
+		return (EINVAL);
+	*rate = pc->pc_clock;
+	return (0);
 }
 
 /*

Modified: stable/7/sys/sparc64/sparc64/mp_locore.S
==============================================================================
--- stable/7/sys/sparc64/sparc64/mp_locore.S	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/mp_locore.S	Thu Mar 19 12:55:11 2009	(r190033)
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/pstate.h>
 #include <machine/smp.h>
 #include <machine/upa.h>
+#include <machine/ver.h>
 
 #include "assym.s"
 
@@ -92,7 +93,7 @@ ENTRY(mp_startup)
 
 	SET(cpu_start_args, %l1, %l0)
 
-	mov	CPU_CLKSYNC, %l1
+	mov	CPU_TICKSYNC, %l1
 	membar	#StoreLoad
 	stw	%l1, [%l0 + CSA_STATE]
 
@@ -101,7 +102,25 @@ ENTRY(mp_startup)
 	 nop
 	wrpr	%l1, 0, %tick
 
-	UPA_GET_MID(%o0)
+	rdpr	%ver, %l1
+	stx	%l1, [%l0 + CSA_VER]
+
+	srlx	%l1, VER_IMPL_SHIFT, %l1
+	sll	%l1, VER_IMPL_SIZE, %l1
+	srl	%l1, VER_IMPL_SIZE, %l1
+	cmp	%l1, CPU_IMPL_ULTRASPARCIII
+	bl	%icc, 3f
+	 nop
+	mov	CPU_STICKSYNC, %l1
+	membar	#StoreLoad
+	stw	%l1, [%l0 + CSA_STATE]
+
+2:	ldx	[%l0 + CSA_STICK], %l1
+	brz	%l1, 2b
+	 nop
+	wr	%l1, 0, %asr24
+
+3:	UPA_GET_MID(%o0)
 
 #if KTR_COMPILE & KTR_SMP
 	CATR(KTR_SMP, "mp_start: CPU %d entered kernel"
@@ -110,9 +129,6 @@ ENTRY(mp_startup)
 9:
 #endif
 
-	rdpr	%ver, %l1
-	stx	%l1, [%l0 + CSA_VER]
-
 	/*
 	 * Inform the boot processor we have inited.
 	 */
@@ -123,9 +139,9 @@ ENTRY(mp_startup)
 	/*
 	 * Wait till its our turn to bootstrap.
 	 */
-2:	lduw	[%l0 + CSA_MID], %l1
+4:	lduw	[%l0 + CSA_MID], %l1
 	cmp	%l1, %o0
-	bne	%xcc, 2b
+	bne	%xcc, 4b
 	 nop
 
 #if KTR_COMPILE & KTR_SMP
@@ -141,7 +157,7 @@ ENTRY(mp_startup)
 	/*
 	 * Map the per-CPU pages.
 	 */
-3:	sllx	%l2, TTE_SHIFT, %l3
+5:	sllx	%l2, TTE_SHIFT, %l3
 	add	%l1, %l3, %l3
 
 	ldx	[%l3 + TTE_VPN], %l4
@@ -156,7 +172,7 @@ ENTRY(mp_startup)
 
 	add	%l2, 1, %l2
 	cmp	%l2, PCPU_PAGES
-	bne	%xcc, 3b
+	bne	%xcc, 5b
 	 nop
 
 	/*

Modified: stable/7/sys/sparc64/sparc64/mp_machdep.c
==============================================================================
--- stable/7/sys/sparc64/sparc64/mp_machdep.c	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/mp_machdep.c	Thu Mar 19 12:55:11 2009	(r190033)
@@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/asi.h>
 #include <machine/atomic.h>
 #include <machine/bus.h>
+#include <machine/cpu.h>
 #include <machine/md_var.h>
 #include <machine/metadata.h>
 #include <machine/ofw_machdep.h>
@@ -101,7 +102,7 @@ static ih_func_t cpu_ipi_stop;
  * since the other processors will use it before the boot CPU enters the
  * kernel.
  */
-struct	cpu_start_args cpu_start_args = { 0, -1, -1, 0, 0 };
+struct	cpu_start_args cpu_start_args = { 0, -1, -1, 0, 0, 0 };
 struct	ipi_cache_args ipi_cache_args;
 struct	ipi_tlb_args ipi_tlb_args;
 struct	pcb stoppcbs[MAXCPU];
@@ -270,17 +271,25 @@ cpu_mp_start(void)
 		if (OF_getprop(child, "clock-frequency", &clock,
 		    sizeof(clock)) <= 0)
 			panic("%s: can't get clock", __func__);
+		if (clock != PCPU_GET(clock))
+			hardclock_use_stick = 1;
 
 		csa->csa_state = 0;
 		sun4u_startcpu(child, (void *)mp_tramp, 0);
 		s = intr_disable();
-		while (csa->csa_state != CPU_CLKSYNC)
+		while (csa->csa_state != CPU_TICKSYNC)
 			;
 		membar(StoreLoad);
 		csa->csa_tick = rd(tick);
+		if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) {
+			while (csa->csa_state != CPU_STICKSYNC)
+				;
+			membar(StoreLoad);
+			csa->csa_stick = rdstick();
+		}
 		while (csa->csa_state != CPU_INIT)
 			;
-		csa->csa_tick = 0;
+		csa->csa_tick = csa->csa_stick = 0;
 		intr_restore(s);
 
 		cpuid = mp_ncpus++;
@@ -291,6 +300,7 @@ cpu_mp_start(void)
 		pc = (struct pcpu *)(va + (PCPU_PAGES * PAGE_SIZE)) - 1;
 		pcpu_init(pc, cpuid, sizeof(*pc));
 		pc->pc_addr = va;
+		pc->pc_clock = clock;
 		pc->pc_mid = mid;
 		pc->pc_node = child;
 

Modified: stable/7/sys/sparc64/sparc64/tick.c
==============================================================================
--- stable/7/sys/sparc64/sparc64/tick.c	Thu Mar 19 12:52:19 2009	(r190032)
+++ stable/7/sys/sparc64/sparc64/tick.c	Thu Mar 19 12:55:11 2009	(r190033)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2001 Jake Burkholder.
+ * Copyright (c) 2005, 2008 Marius Strobl <marius@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,22 +29,30 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
-#include <sys/kernel.h>
 #include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
 #include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/smp.h>
 #include <sys/sysctl.h>
 #include <sys/timetc.h>
 
-#include <machine/clock.h>
+#include <dev/ofw/openfirm.h>
+
 #include <machine/cpu.h>
 #include <machine/frame.h>
 #include <machine/intr_machdep.h>
 #include <machine/tick.h>
 #include <machine/ver.h>
 
-#define	TICK_GRACE	10000
+/* 10000 ticks proved okay for 500MHz. */
+#define	TICK_GRACE(clock)	((clock) / 1000000 * 2 * 10)
+
+#define	TICK_QUALITY_MP	10
+#define	TICK_QUALITY_UP	1000
 
 SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");
 
@@ -63,7 +72,24 @@ static int adjust_ticks = 0;
 SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
     0, "total number of tick interrupts with adjustment");
 
+u_int hardclock_use_stick = 0;
+SYSCTL_INT(_machdep_tick, OID_AUTO, hardclock_use_stick, CTLFLAG_RD,
+    &hardclock_use_stick, 0, "hardclock uses STICK instead of TICK timer");
+
+static struct timecounter tick_tc;
+static u_long tick_increment;
+
+static uint64_t tick_cputicks(void);
+static timecounter_get_t tick_get_timecount_up;
+#ifdef SMP
+static timecounter_get_t tick_get_timecount_mp;
+#endif
 static void tick_hardclock(struct trapframe *tf);
+static void tick_hardclock_bbwar(struct trapframe *tf);
+static inline void tick_hardclock_common(struct trapframe *tf, u_long tick,
+    u_long adj);
+static inline void tick_process(struct trapframe *tf);
+static void stick_hardclock(struct trapframe *tf);
 
 static uint64_t
 tick_cputicks(void)
@@ -75,9 +101,65 @@ tick_cputicks(void)
 void
 cpu_initclocks(void)
 {
+	uint32_t clock;
 
 	stathz = hz;
+
+	/*
+	 * Given that the STICK timers typically are driven at rather low
+	 * frequencies they shouldn't be used except when really necessary.
+	 */
+	if (hardclock_use_stick != 0) {
+		if (OF_getprop(OF_parent(PCPU_GET(node)), "stick-frequency",
+		    &clock, sizeof(clock)) == -1)
+		panic("%s: could not determine STICK frequency", __func__);
+		intr_setup(PIL_TICK, stick_hardclock, -1, NULL, NULL);
+		/*
+		 * We don't provide a CPU ticker as long as the frequency
+		 * supplied isn't actually used per-CPU.
+		 */
+	} else {
+		clock = PCPU_GET(clock);
+		intr_setup(PIL_TICK, cpu_impl < CPU_IMPL_ULTRASPARCIII ?
+		    tick_hardclock_bbwar : tick_hardclock, -1, NULL, NULL);
+		set_cputicker(tick_cputicks, clock, 0);
+	}
+	tick_increment = clock / hz;
+	/*
+	 * Avoid stopping of hardclock in terms of a lost (S)TICK interrupt
+	 * by ensuring that the (S)TICK period is at least TICK_GRACE ticks.
+	 */
+	if (tick_increment < TICK_GRACE(clock))
+		panic("%s: HZ too high, decrease to at least %d",
+		    __func__, clock / TICK_GRACE(clock));
 	tick_start();
+
+	/*
+	 * Initialize the TICK-based timecounter.  This must not happen
+	 * before SI_SUB_INTRINSIC for tick_get_timecount_mp() to work.
+	 */
+	tick_tc.tc_get_timecount = tick_get_timecount_up;
+	tick_tc.tc_poll_pps = NULL;
+	tick_tc.tc_counter_mask = ~0u;
+	tick_tc.tc_frequency = PCPU_GET(clock);
+	tick_tc.tc_name = "tick";
+	tick_tc.tc_quality = TICK_QUALITY_UP;
+	tick_tc.tc_priv = NULL;
+#ifdef SMP
+	/*
+	 * We (try to) sync the (S)TICK timers of APs with the BSP during
+	 * their startup but not afterwards.  The resulting drift can
+	 * cause problems when the time is calculated based on (S)TICK
+	 * values read on different CPUs.  Thus we bind to the BSP for
+	 * reading the register and use a low quality for the otherwise
+	 * high quality (S)TICK timers in the MP case.
+	 */
+	if (cpu_mp_probe()) {
+		tick_tc.tc_get_timecount = tick_get_timecount_mp;
+		tick_tc.tc_quality = TICK_QUALITY_MP;
+	}
+#endif
+	tc_init(&tick_tc);
 }
 
 static inline void
@@ -93,27 +175,68 @@ tick_process(struct trapframe *tf)
 	statclock(TRAPF_USERMODE(tf));
 }
 
+/*
+ * NB: the sequence of reading the (S)TICK register, calculating the value
+ * of the next tick and writing it to the (S)TICK_COMPARE register must not
+ * be interrupted, not even by an IPI, otherwise a value that is in the past
+ * could be written in the worst case, causing hardclock to stop.
+ */
+
 static void
 tick_hardclock(struct trapframe *tf)
 {
-	u_long adj, ref, tick;
-	long delta;
+	u_long adj, tick;
+	register_t s;
+
+	critical_enter();
+	adj = PCPU_GET(tickadj);
+	s = intr_disable();
+	tick = rd(tick);
+	wr(tick_cmpr, tick + tick_increment - adj, 0);
+	intr_restore(s);
+	tick_hardclock_common(tf, tick, adj);
+	critical_exit();
+}
+
+static void
+tick_hardclock_bbwar(struct trapframe *tf)
+{
+	u_long adj, tick;
 	register_t s;
-	int count;
 
-	/*
-	 * The sequence of reading the TICK register, calculating the value
-	 * of the next tick and writing it to the TICK_CMPR register must not
-	 * be interrupted, not even by an IPI, otherwise a value that is in
-	 * the past could be written in the worst case, causing hardclock to
-	 * stop.
-	 */
 	critical_enter();
 	adj = PCPU_GET(tickadj);
 	s = intr_disable();
 	tick = rd(tick);
 	wrtickcmpr(tick + tick_increment - adj, 0);
 	intr_restore(s);
+	tick_hardclock_common(tf, tick, adj);
+	critical_exit();
+}
+
+static void
+stick_hardclock(struct trapframe *tf)
+{
+	u_long adj, stick;
+	register_t s;
+
+	critical_enter();
+	adj = PCPU_GET(tickadj);
+	s = intr_disable();
+	stick = rdstick();
+	wrstickcmpr(stick + tick_increment - adj, 0);
+	intr_restore(s);
+	tick_hardclock_common(tf, stick, adj);
+	critical_exit();
+}
+
+static inline void
+tick_hardclock_common(struct trapframe *tf, u_long tick, u_long adj)
+{
+	u_long ref;
+	long delta;
+	int count;
+
 	ref = PCPU_GET(tickref);
 	delta = tick - ref;
 	count = 0;
@@ -139,29 +262,36 @@ tick_hardclock(struct trapframe *tf)
 	}
 	PCPU_SET(tickref, ref);
 	PCPU_SET(tickadj, adj);
-	critical_exit();
 }
 
-void
-tick_init(u_long clock)
+static u_int
+tick_get_timecount_up(struct timecounter *tc)
 {
 
-	tick_freq = clock;
-	tick_MHz = clock / 1000000;
-	tick_increment = clock / hz;
+	return ((u_int)rd(tick));
+}
 
-	/*
-	 * UltraSparc II[e,i] based systems come up with the tick interrupt
-	 * enabled and a handler that resets the tick counter, causing DELAY()
-	 * to not work properly when used early in boot.
-	 * UltraSPARC III based systems come up with the system tick interrupt
-	 * enabled, causing an interrupt storm on startup since they are not
-	 * handled.
-	 */
-	tick_stop();
+#ifdef SMP
+static u_int
+tick_get_timecount_mp(struct timecounter *tc)
+{
+	struct thread *td;
+	u_int tick;
+
+	td = curthread;
+	thread_lock(td);
+	sched_bind(td, 0);
+	thread_unlock(td);
+
+	tick = tick_get_timecount_up(tc);
+
+	thread_lock(td);
+	sched_unbind(td);
+	thread_unlock(td);
 
-	set_cputicker(tick_cputicks, tick_freq, 0);
+	return (tick);
 }
+#endif
 
 void
 tick_start(void)
@@ -170,32 +300,34 @@ tick_start(void)
 	register_t s;
 
 	/*
-	 * Avoid stopping of hardclock in terms of a lost tick interrupt
-	 * by ensuring that the tick period is at least TICK_GRACE ticks.
-	 * This check would be better placed in tick_init(), however we
-	 * have to call tick_init() before cninit() in order to provide
-	 * the low-level console drivers with a working DELAY() which in
-	 * turn means we cannot use panic() in tick_init().
-	 */
-	if (tick_increment < TICK_GRACE)
-		panic("%s: HZ too high, decrease to at least %ld", __func__,
-		    tick_freq / TICK_GRACE);
-
-	if (curcpu == 0)
-		intr_setup(PIL_TICK, tick_hardclock, -1, NULL, NULL);
-
-	/*
-	 * Try to make the tick interrupts as synchronously as possible on
-	 * all CPUs to avoid inaccuracies for migrating processes. Leave out
-	 * one tick to make sure that it is not missed.
+	 * Try to make the (S)TICK interrupts as synchronously as possible
+	 * on all CPUs to avoid inaccuracies for migrating processes.  Leave
+	 * out one tick to make sure that it is not missed.
 	 */
+	critical_enter();
 	PCPU_SET(tickadj, 0);
 	s = intr_disable();
-	base = rd(tick);
+	if (hardclock_use_stick != 0)
+		base = rdstick();
+	else
+		base = rd(tick);
 	base = roundup(base, tick_increment);
 	PCPU_SET(tickref, base);
-	wrtickcmpr(base + tick_increment, 0);
+	if (hardclock_use_stick != 0)
+		wrstickcmpr(base + tick_increment, 0);
+	else
+		wrtickcmpr(base + tick_increment, 0);
 	intr_restore(s);
+	critical_exit();
+}
+
+void
+tick_clear(void)
+{
+
+	if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
+		wrstick(0, 0);
+	wrpr(tick, 0, 0);
 }
 
 void
@@ -203,6 +335,6 @@ tick_stop(void)
 {
 
 	if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
-		wr(asr25, 1L << 63, 0);
+		wrstickcmpr(1L << 63, 0);
 	wrtickcmpr(1L << 63, 0);
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903191255.n2JCtB7p061565>