linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Pentium4 local APIC NMI watchdog for 2.4.15-pre3
@ 2001-11-11 22:38 Mikael Pettersson
  0 siblings, 0 replies; only message in thread
From: Mikael Pettersson @ 2001-11-11 22:38 UTC (permalink / raw)
  To: linux-kernel

This patch adds local APIC NMI watchdog support for the Pentium4,
in a manner similar to the existing support for P6 and K7.
The patch also includes two minor cleanups:
- replaced a number of for() loops to clear MSRs in nmi.c
  by calls to a new local procedure
- renamed MSR_IA32_PERFCTR0/PERFCTR1/EVNTSEL0/EVNTSEL1 to
  MSR_P6_PERFCTR0/PERFCTR1/EVNTSEL0/EVNTSEL1 since these MSRs
  are P6-specific

/Mikael

--- linux-2.4.15-pre3-p4watchdog/arch/i386/kernel/nmi.c.~1~	Sun Sep 23 21:06:30 2001
+++ linux-2.4.15-pre3-p4watchdog/arch/i386/kernel/nmi.c	Sun Nov 11 22:58:00 2001
@@ -8,6 +8,7 @@
  *  Fixes:
  *  Mikael Pettersson	: AMD K7 support for local APIC NMI watchdog.
  *  Mikael Pettersson	: Power Management for local APIC NMI watchdog.
+ *  Mikael Pettersson	: Pentium 4 support for local APIC NMI watchdog.
  */
 
 #include <linux/config.h>
@@ -25,6 +26,7 @@
 #include <asm/mpspec.h>
 
 unsigned int nmi_watchdog = NMI_NONE;
+static unsigned char nmi_is_p4;
 static unsigned int nmi_hz = HZ;
 unsigned int nmi_perfctr_msr;	/* the MSR to reset in NMI handler */
 extern void show_registers(struct pt_regs *regs);
@@ -43,6 +45,31 @@
 #define P6_EVENT_CPU_CLOCKS_NOT_HALTED	0x79
 #define P6_NMI_EVENT		P6_EVENT_CPU_CLOCKS_NOT_HALTED
 
+#define MSR_P4_MISC_ENABLE	0x1A0
+#define MSR_P4_MISC_ENABLE_PMAVAIL	(1<<7)
+#define MSR_P4_PERFCTR0		0x300
+#define MSR_P4_CCCR0		0x360
+#define P4_ESCR_EVENT_SELECT(N)	((N)<<25)
+#define P4_ESCR_OS		(1<<3)
+#define P4_ESCR_USR		(1<<2)
+#define P4_CCCR_OVF_PMI		(1<<26)
+#define P4_CCCR_THRESHOLD(N)	((N)<<20)
+#define P4_CCCR_COMPLEMENT	(1<<19)
+#define P4_CCCR_COMPARE		(1<<18)
+#define P4_CCCR_REQUIRED	(3<<16)
+#define P4_CCCR_ESCR_SELECT(N)	((N)<<13)
+#define P4_CCCR_ENABLE		(1<<12)
+/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter
+   CRU_ESCR0 (with any non-null event selector) through a complemented
+   max threshold. [IA32-Vol3, Section 14.9.9] */
+#define MSR_P4_IQ_COUNTER0	0x30C
+#define MSR_P4_IQ_CCCR0		0x36C
+#define MSR_P4_CRU_ESCR0	0x3B8
+#define P4_NMI_CRU_ESCR0	(P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR)
+#define P4_NMI_IQ_CCCR0	\
+	(P4_CCCR_OVF_PMI|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT|	\
+	 P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE)
+
 int __init check_nmi_watchdog (void)
 {
 	irq_cpustat_t tmp[NR_CPUS];
@@ -84,11 +111,11 @@
 	/*
 	 * If any other x86 CPU has a local APIC, then
 	 * please test the NMI stuff there and send me the
-	 * missing bits. Right now Intel P6 and AMD K7 only.
+	 * missing bits. Right now Intel P6/P4 and AMD K7 only.
 	 */
 	if ((nmi == NMI_LOCAL_APIC) &&
 			(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
-			(boot_cpu_data.x86 == 6))
+			(boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15))
 		nmi_watchdog = nmi;
 	if ((nmi == NMI_LOCAL_APIC) &&
 			(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) &&
@@ -118,7 +145,15 @@
 		wrmsr(MSR_K7_EVNTSEL0, 0, 0);
 		break;
 	case X86_VENDOR_INTEL:
-		wrmsr(MSR_IA32_EVNTSEL0, 0, 0);
+		switch (boot_cpu_data.x86) {
+		case 6:
+			wrmsr(MSR_P6_EVNTSEL0, 0, 0);
+			break;
+		case 15:
+			wrmsr(MSR_P4_IQ_CCCR0, 0, 0);
+			wrmsr(MSR_P4_CRU_ESCR0, 0, 0);
+			break;
+		}
 		break;
 	}
 }
@@ -157,17 +192,22 @@
  * Original code written by Keith Owens.
  */
 
+static void __pminit clear_msr_range(unsigned int base, unsigned int n)
+{
+	unsigned int i;
+
+	for(i = 0; i < n; ++i)
+		wrmsr(base+i, 0, 0);
+}
+
 static void __pminit setup_k7_watchdog(void)
 {
-	int i;
 	unsigned int evntsel;
 
 	nmi_perfctr_msr = MSR_K7_PERFCTR0;
 
-	for(i = 0; i < 4; ++i) {
-		wrmsr(MSR_K7_EVNTSEL0+i, 0, 0);
-		wrmsr(MSR_K7_PERFCTR0+i, 0, 0);
-	}
+	clear_msr_range(MSR_K7_EVNTSEL0, 4);
+	clear_msr_range(MSR_K7_PERFCTR0, 4);
 
 	evntsel = K7_EVNTSEL_INT
 		| K7_EVNTSEL_OS
@@ -184,27 +224,52 @@
 
 static void __pminit setup_p6_watchdog(void)
 {
-	int i;
 	unsigned int evntsel;
 
-	nmi_perfctr_msr = MSR_IA32_PERFCTR0;
+	nmi_perfctr_msr = MSR_P6_PERFCTR0;
 
-	for(i = 0; i < 2; ++i) {
-		wrmsr(MSR_IA32_EVNTSEL0+i, 0, 0);
-		wrmsr(MSR_IA32_PERFCTR0+i, 0, 0);
-	}
+	clear_msr_range(MSR_P6_EVNTSEL0, 2);
+	clear_msr_range(MSR_P6_PERFCTR0, 2);
 
 	evntsel = P6_EVNTSEL_INT
 		| P6_EVNTSEL_OS
 		| P6_EVNTSEL_USR
 		| P6_NMI_EVENT;
 
-	wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0);
-	Dprintk("setting IA32_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
-	wrmsr(MSR_IA32_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0);
+	wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
+	Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
+	wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0);
 	apic_write(APIC_LVTPC, APIC_DM_NMI);
 	evntsel |= P6_EVNTSEL0_ENABLE;
-	wrmsr(MSR_IA32_EVNTSEL0, evntsel, 0);
+	wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
+}
+
+static int __pminit setup_p4_watchdog(void)
+{
+	unsigned int misc_enable, dummy;
+
+	rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy);
+	if (!(misc_enable & MSR_P4_MISC_ENABLE_PMAVAIL))
+		return 0;
+
+	nmi_perfctr_msr = MSR_P4_IQ_COUNTER0;
+	nmi_is_p4 = 1;
+
+	clear_msr_range(MSR_P4_PERFCTR0, 18);
+	clear_msr_range(MSR_P4_CCCR0, 18);
+	clear_msr_range(0x3A0, (0x3BE - 0x3A0)+1);
+	clear_msr_range(0x3C0, (0x3C5 - 0x3C0)+1);
+	clear_msr_range(0x3C8, (0x3CD - 0x3C8)+1);
+	clear_msr_range(0x3E0, (0x3E1 - 0x3E0)+1);
+	/* XXX: should we also clear 0x3F0-0x3F2 ? */
+
+	wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0);
+	wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0);
+	Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000));
+	wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1);
+	apic_write(APIC_LVTPC, APIC_DM_NMI);
+	wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0);
+	return 1;
 }
 
 void __pminit setup_apic_nmi_watchdog (void)
@@ -216,9 +281,17 @@
 		setup_k7_watchdog();
 		break;
 	case X86_VENDOR_INTEL:
-		if (boot_cpu_data.x86 != 6)
+		switch (boot_cpu_data.x86) {
+		case 6:
+			setup_p6_watchdog();
+			break;
+		case 15:
+			if (!setup_p4_watchdog())
+				return;
+			break;
+		default:
 			return;
-		setup_p6_watchdog();
+		}
 		break;
 	default:
 		return;
@@ -295,6 +368,18 @@
 		last_irq_sums[cpu] = sum;
 		alert_counter[cpu] = 0;
 	}
-	if (nmi_perfctr_msr)
+	if (nmi_perfctr_msr) {
+		if (nmi_is_p4) {
+			/*
+			 * P4 quirks:
+			 * - An overflown perfctr will assert its interrupt
+			 *   until the OVF flag in its CCCR is cleared.
+			 * - LVTPC is masked on interrupt and must be
+			 *   unmasked by the LVTPC handler.
+			 */
+			wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0);
+			apic_write(APIC_LVTPC, APIC_DM_NMI);
+		}
 		wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
+	}
 }
--- linux-2.4.15-pre3-p4watchdog/include/asm-i386/msr.h.~1~	Sun Sep 23 21:06:37 2001
+++ linux-2.4.15-pre3-p4watchdog/include/asm-i386/msr.h	Sun Nov 11 22:58:00 2001
@@ -48,18 +48,12 @@
 #define MSR_IA32_UCODE_WRITE		0x79
 #define MSR_IA32_UCODE_REV		0x8b
 
-#define MSR_IA32_PERFCTR0		0xc1
-#define MSR_IA32_PERFCTR1		0xc2
-
 #define MSR_IA32_BBL_CR_CTL		0x119
 
 #define MSR_IA32_MCG_CAP		0x179
 #define MSR_IA32_MCG_STATUS		0x17a
 #define MSR_IA32_MCG_CTL		0x17b
 
-#define MSR_IA32_EVNTSEL0		0x186
-#define MSR_IA32_EVNTSEL1		0x187
-
 #define MSR_IA32_DEBUGCTLMSR		0x1d9
 #define MSR_IA32_LASTBRANCHFROMIP	0x1db
 #define MSR_IA32_LASTBRANCHTOIP		0x1dc
@@ -71,6 +65,11 @@
 #define MSR_IA32_MC0_ADDR		0x402
 #define MSR_IA32_MC0_MISC		0x403
 
+#define MSR_P6_PERFCTR0			0xc1
+#define MSR_P6_PERFCTR1			0xc2
+#define MSR_P6_EVNTSEL0			0x186
+#define MSR_P6_EVNTSEL1			0x187
+
 /* AMD Defined MSRs */
 #define MSR_K6_EFER			0xC0000080
 #define MSR_K6_STAR			0xC0000081

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2001-11-11 22:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-11-11 22:38 [PATCH] Pentium4 local APIC NMI watchdog for 2.4.15-pre3 Mikael Pettersson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).