From: Jaya Kumar This patch by Jaya Kumar introduces a generic infrastructure to deal with x86 chipsets with nonstandard reset sequences, and adds support for the Geode gx1/cs5530a chipset. Signed-off-by: Jaya Kumar Signed-off-by: H. Peter Anvin Signed-off-by: Andrew Morton --- 25-akpm/arch/i386/Kconfig | 18 +++++++++ 25-akpm/arch/i386/kernel/Makefile | 1 25-akpm/arch/i386/kernel/reboot.c | 2 + 25-akpm/arch/i386/kernel/reboot_fixups.c | 56 +++++++++++++++++++++++++++++++ 25-akpm/include/linux/reboot_fixups.h | 10 +++++ 5 files changed, 87 insertions(+) diff -puN arch/i386/Kconfig~x86-reboot-add-reboot-fixup-for-gx1-cs5530a arch/i386/Kconfig --- 25/arch/i386/Kconfig~x86-reboot-add-reboot-fixup-for-gx1-cs5530a 2005-04-11 21:04:44.000000000 -0700 +++ 25-akpm/arch/i386/Kconfig 2005-04-11 21:04:44.000000000 -0700 @@ -653,6 +653,24 @@ config I8K Say Y if you intend to run this kernel on a Dell Inspiron 8000. Say N otherwise. +config X86_REBOOTFIXUPS + bool "Enable X86 board specific fixups for reboot" + depends on X86 + default n + ---help--- + This enables chipset and/or board specific fixups to be done + in order to get reboot to work correctly. This is only needed on + some combinations of hardware and BIOS. The symptom, for which + this config is intended, is when reboot ends with a stalled/hung + system. + + Currently, the only fixup is for the Geode GX1/CS5530A/TROM2.1. + combination. + + Say Y if you want to enable the fixup. Currently, it's safe to + enable this option even if you don't need it. + Say N otherwise. + config MICROCODE tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support" ---help--- diff -puN arch/i386/kernel/Makefile~x86-reboot-add-reboot-fixup-for-gx1-cs5530a arch/i386/kernel/Makefile --- 25/arch/i386/kernel/Makefile~x86-reboot-add-reboot-fixup-for-gx1-cs5530a 2005-04-11 21:04:44.000000000 -0700 +++ 25-akpm/arch/i386/kernel/Makefile 2005-04-11 21:04:44.000000000 -0700 @@ -23,6 +23,7 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoli obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o +obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups.o obj-$(CONFIG_X86_NUMAQ) += numaq.o obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o obj-$(CONFIG_KPROBES) += kprobes.o diff -puN arch/i386/kernel/reboot.c~x86-reboot-add-reboot-fixup-for-gx1-cs5530a arch/i386/kernel/reboot.c --- 25/arch/i386/kernel/reboot.c~x86-reboot-add-reboot-fixup-for-gx1-cs5530a 2005-04-11 21:04:44.000000000 -0700 +++ 25-akpm/arch/i386/kernel/reboot.c 2005-04-11 21:04:44.000000000 -0700 @@ -13,6 +13,7 @@ #include #include #include "mach_reboot.h" +#include /* * Power off function, if any @@ -348,6 +349,7 @@ void machine_restart(char * __unused) /* rebooting needs to touch the page at absolute addr 0 */ *((unsigned short *)__va(0x472)) = reboot_mode; for (;;) { + mach_reboot_fixups(); /* for board specific fixups */ mach_reboot(); /* That didn't work - force a triple fault.. */ __asm__ __volatile__("lidt %0": :"m" (no_idt)); diff -puN /dev/null arch/i386/kernel/reboot_fixups.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/i386/kernel/reboot_fixups.c 2005-04-11 21:04:44.000000000 -0700 @@ -0,0 +1,56 @@ +/* + * linux/arch/i386/kernel/reboot_fixups.c + * + * This is a good place to put board specific reboot fixups. + * + * List of supported fixups: + * geode-gx1/cs5530a - Jaya Kumar + * + */ + +#include +#include + +static void cs5530a_warm_reset(struct pci_dev *dev) +{ + /* writing 1 to the reset control register, 0x44 causes the + cs5530a to perform a system warm reset */ + pci_write_config_byte(dev, 0x44, 0x1); + udelay(50); /* shouldn't get here but be safe and spin-a-while */ + return; +} + +struct device_fixup { + unsigned int vendor; + unsigned int device; + void (*reboot_fixup)(struct pci_dev *); +}; + +static struct device_fixup fixups_table[] = { +{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset }, +}; + +/* + * we see if any fixup is available for our current hardware. if there + * is a fixup, we call it and we expect to never return from it. if we + * do return, we keep looking and then eventually fall back to the + * standard mach_reboot on return. + */ +void mach_reboot_fixups(void) +{ + struct device_fixup *cur; + struct pci_dev *dev; + int i; + + for (i=0; i < (sizeof(fixups_table)/sizeof(fixups_table[0])); i++) { + cur = &(fixups_table[i]); + dev = pci_get_device(cur->vendor, cur->device, 0); + if (!dev) + continue; + + cur->reboot_fixup(dev); + } + + printk(KERN_WARNING "No reboot fixup found for your hardware\n"); +} + diff -puN /dev/null include/linux/reboot_fixups.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/linux/reboot_fixups.h 2005-04-11 21:04:44.000000000 -0700 @@ -0,0 +1,10 @@ +#ifndef _LINUX_REBOOT_FIXUPS_H +#define _LINUX_REBOOT_FIXUPS_H + +#ifdef CONFIG_X86_REBOOTFIXUPS +extern void mach_reboot_fixups(void); +#else +#define mach_reboot_fixups() ((void)(0)) +#endif + +#endif /* _LINUX_REBOOT_FIXUPS_H */ _