Subject: port-i386/36579: some improvements for the i386 cpu_reset() code
To: None <port-i386-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: Greg A. Woods <woods@planix.com>
List: netbsd-bugs
Date: 06/29/2007 19:40:01
>Number: 36579
>Category: port-i386
>Synopsis: some improvements for the i386 cpu_reset() code
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: port-i386-maintainer
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Jun 29 19:40:00 +0000 2007
>Originator: Greg A. Woods
>Release: netbsd-4
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
System: NetBSD 4.0BETA2
Architecture: i386
Machine: i386
>Description:
>How-To-Repeat:
examine the related code in other operating systems
>Fix:
warning: patch line numbers are off!
see also the last few lines of change to cpu_reboot() in PR#
port-i386/36578
it might be quite helpful to add DEBUG or even DIAGNOSTIC
printf()s to show which methods fail on a given machine....
Index: sys/arch/i386/i386/machdep.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/arch/i386/i386/machdep.c,v
retrieving revision 1.586.2.4
diff -u -r1.586.2.4 machdep.c
--- sys/arch/i386/i386/machdep.c 20 Apr 2007 20:31:25 -0000 1.586.2.4
+++ sys/arch/i386/i386/machdep.c 29 Jun 2007 19:20:14 -0000
@@ -2184,10 +2212,48 @@
#include <dev/ic/mc146818reg.h> /* for NVRAM POST */
#include <i386/isa/nvram.h> /* for NVRAM POST */
+/* XXX some copied from sys/arch/x86/pci/pci_machdep.c, should be in <x86/include/pci_machdep.h> */
+/* XXX they're also used in sys/arch/i386/stand/lib/test/pci_user.c */
+#define PCI_MODE1_ENABLE 0x80000000UL
+#define PCI_MODE1_ADDRESS_REG 0x0cf8
+
+#define PCI_MODE1_RESET_CTL_REG 0x0cf9
+#define PCI_MODE1_DATA_REG 0x0cfc
+
+/*
+ * From Radisys 82600 High Integration Dual PCI System Controller Data Book:
+ *
+ * Bits 1 and 2 in this register are used by the 82600 to generate a hard reset
+ * or a soft reset. During a hard reset, the 82600 asserts CPURST# and LPRST#
+ * and resets its own core logic. BPRST# is also asserted if the 82600 is
+ * configured as the BPCI Central Resource. During a soft reset, the 82600
+ * only asserts INIT#.
+ *
+ * Bit Description
+ *
+ * 7:3 Reserved.
+ *
+ * 2 Reset CPU (RCPU) R/W. A transition of this bit from a 0 to a 1
+ * initiates a reset. The type of reset is determined by bit 1. This bit
+ * cannot be read as a 1.
+ *
+ * 1 System Reset (SRST) R/W. This bit is used to select the type of
+ * reset generated when bit 2 in this register transitions to a 1. A
+ * value of 1 selects a hard reset and 0 selects a soft reset
+ *
+ * 0 Reserved.
+ */
+#define PCI_RESET_RCPU (1 << 2)
+#define PCI_RESET_SRST (1 << 1)
+
+#define PCAT_SYS_CTL_A 0x92 /* AT System Control Port A */
+#define PCAT_SYS_CTL_A_FRST 0x01 /* Fast Reset, aka Fast Init */
+
void
cpu_reset()
{
struct region_descriptor region;
+ u_int8_t reg8;
disable_intr();
@@ -2213,15 +2279,50 @@
* See AMD Geode SC1100 Processor Data Book, Revision 2.0,
* sections 6.3.1, 6.3.2, and 6.4.1.
*/
- if (cpu_info_primary.ci_signature == 0x540) {
- outl(0xcf8, 0x80009044ul);
- outl(0xcfc, 0xf);
+ if (cpu_info_primary.ci_signature == 0x540) { /* CPU_GEODE1100??? */
+ outl(PCI_MODE1_ADDRESS_REG, PCI_MODE1_ENABLE | 0x9044);
+ outl(PCI_MODE1_DATA_REG, 0xf);
+ }
+
+ /*
+ * Try the PCI system & cpu reset _first_ (from FreeBSD and GNU/Linux)
+ *
+ * Write 0x6 to PCI Reset Control Register (0xcf9) to reset the CPU,
+ * the PCI controller itself, and to trigger a system-wide reset.
+ *
+ * This is the best method for all recent and modern systems that
+ * include any form of PCI controller.
+ */
+ reg8 = inb(PCI_MODE1_RESET_CTL_REG);
+ reg8 |= PCI_RESET_RCPU | PCI_RESET_SRST;
+ outb(PCI_MODE1_RESET_CTL_REG, reg8);
+ delay(500000); /* wait 0.5 sec to see if that did it */
+
+ /*
+ * Try reset by setting the 0x01 bit of the System Control Port A
+ * (0x92) (also from FreeBSD)
+ *
+ * This is the second-best way to reset any i386 system, and should
+ * work on everything back to the PC/AT.
+ *
+ * Note this doesn't work (or didn't) in VMware.
+ */
+ reg8 = inb(PCAT_SYS_CTL_A);
+ /* Check the the hardware actually has the port in question */
+ if (reg8 != 0xff) {
+ /* FAST_INIT must be zero before a one can be written */
+ if ((reg8 & PCAT_SYS_CTL_A_FRST) != 0)
+ outb(PCAT_SYS_CTL_A, reg8 & ~PCAT_SYS_CTL_A_FRST);
+ outb(PCAT_SYS_CTL_A, reg8 | PCAT_SYS_CTL_A_FRST);
+ delay(500000); /* wait 0.5 sec to see if that did it */
}
/*
- * The keyboard controller has 4 random output pins, one of which is
- * connected to the RESET pin on the CPU in many PCs. We tell the
- * keyboard controller to pulse this line a couple of times.
+ * The keyboard controller has 4 output pins, one of which is connected
+ * to the CPU RESET line in many PCs. We tell the keyboard controller
+ * to pulse this line a couple of times.
+ *
+ * XXX FreeBSD only does it once, then waits for 0.5 sec
*/
outb(IO_KBD + KBCMDP, KBC_PULSE0);
delay(100000);
@@ -2237,7 +2338,7 @@
lidt(®ion);
__asm volatile("divl %0,%1" : : "q" (0), "a" (0));
-#if 0
+#if 0 /* XXX FreeBSD actually does resort to this as a last go */
/*
* Try to cause a triple fault and watchdog reset by unmapping the
* entire address space and doing a TLB flush.
@@ -2246,7 +2347,6 @@
tlbflush();
#endif
- for (;;);
}
void
>Unformatted: