1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
29a163ed8SThomas Gleixner /*
39a163ed8SThomas Gleixner  * This is a good place to put board specific reboot fixups.
49a163ed8SThomas Gleixner  *
59a163ed8SThomas Gleixner  * List of supported fixups:
69a163ed8SThomas Gleixner  * geode-gx1/cs5530a - Jaya Kumar <jayalk@intworks.biz>
79a163ed8SThomas Gleixner  * geode-gx/lx/cs5536 - Andres Salomon <dilinger@debian.org>
89a163ed8SThomas Gleixner  *
99a163ed8SThomas Gleixner  */
109a163ed8SThomas Gleixner 
119a163ed8SThomas Gleixner #include <asm/delay.h>
129a163ed8SThomas Gleixner #include <linux/pci.h>
133d034aecSDenis V. Lunev #include <linux/interrupt.h>
149a163ed8SThomas Gleixner #include <asm/reboot_fixups.h>
159a163ed8SThomas Gleixner #include <asm/msr.h>
162e8c1243SAndres Salomon #include <linux/cs5535.h>
179a163ed8SThomas Gleixner 
cs5530a_warm_reset(struct pci_dev * dev)189a163ed8SThomas Gleixner static void cs5530a_warm_reset(struct pci_dev *dev)
199a163ed8SThomas Gleixner {
209a163ed8SThomas Gleixner 	/* writing 1 to the reset control register, 0x44 causes the
219a163ed8SThomas Gleixner 	cs5530a to perform a system warm reset */
229a163ed8SThomas Gleixner 	pci_write_config_byte(dev, 0x44, 0x1);
239a163ed8SThomas Gleixner 	udelay(50); /* shouldn't get here but be safe and spin-a-while */
249a163ed8SThomas Gleixner 	return;
259a163ed8SThomas Gleixner }
269a163ed8SThomas Gleixner 
cs5536_warm_reset(struct pci_dev * dev)279a163ed8SThomas Gleixner static void cs5536_warm_reset(struct pci_dev *dev)
289a163ed8SThomas Gleixner {
290387f451SAndres Salomon 	/* writing 1 to the LSB of this MSR causes a hard reset */
300387f451SAndres Salomon 	wrmsrl(MSR_DIVIL_SOFT_RESET, 1ULL);
319a163ed8SThomas Gleixner 	udelay(50); /* shouldn't get here but be safe and spin a while */
329a163ed8SThomas Gleixner }
339a163ed8SThomas Gleixner 
rdc321x_reset(struct pci_dev * dev)34ff472a11SFlorian Fainelli static void rdc321x_reset(struct pci_dev *dev)
35ff472a11SFlorian Fainelli {
36ff472a11SFlorian Fainelli 	unsigned i;
37ff472a11SFlorian Fainelli 	/* Voluntary reset the watchdog timer */
38ff472a11SFlorian Fainelli 	outl(0x80003840, 0xCF8);
39ff472a11SFlorian Fainelli 	/* Generate a CPU reset on next tick */
40ff472a11SFlorian Fainelli 	i = inl(0xCFC);
41ff472a11SFlorian Fainelli 	/* Use the minimum timer resolution */
42ff472a11SFlorian Fainelli 	i |= 0x1600;
43ff472a11SFlorian Fainelli 	outl(i, 0xCFC);
44ff472a11SFlorian Fainelli 	outb(1, 0x92);
45ff472a11SFlorian Fainelli }
46ff472a11SFlorian Fainelli 
ce4100_reset(struct pci_dev * dev)4737bc9f50SDirk Brandewie static void ce4100_reset(struct pci_dev *dev)
4837bc9f50SDirk Brandewie {
4937bc9f50SDirk Brandewie 	int i;
5037bc9f50SDirk Brandewie 
5137bc9f50SDirk Brandewie 	for (i = 0; i < 10; i++) {
5237bc9f50SDirk Brandewie 		outb(0x2, 0xcf9);
5337bc9f50SDirk Brandewie 		udelay(50);
5437bc9f50SDirk Brandewie 	}
5537bc9f50SDirk Brandewie }
5637bc9f50SDirk Brandewie 
579a163ed8SThomas Gleixner struct device_fixup {
589a163ed8SThomas Gleixner 	unsigned int vendor;
599a163ed8SThomas Gleixner 	unsigned int device;
609a163ed8SThomas Gleixner 	void (*reboot_fixup)(struct pci_dev *);
619a163ed8SThomas Gleixner };
629a163ed8SThomas Gleixner 
6337bc9f50SDirk Brandewie /*
6437bc9f50SDirk Brandewie  * PCI ids solely used for fixups_table go here
6537bc9f50SDirk Brandewie  */
6637bc9f50SDirk Brandewie #define PCI_DEVICE_ID_INTEL_CE4100	0x0708
6737bc9f50SDirk Brandewie 
68ebdd561aSJan Beulich static const struct device_fixup fixups_table[] = {
699a163ed8SThomas Gleixner { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset },
709a163ed8SThomas Gleixner { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset },
716d1b30e3SDenys { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE, cs5530a_warm_reset },
72ff472a11SFlorian Fainelli { PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030, rdc321x_reset },
7337bc9f50SDirk Brandewie { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100, ce4100_reset },
749a163ed8SThomas Gleixner };
759a163ed8SThomas Gleixner 
769a163ed8SThomas Gleixner /*
779a163ed8SThomas Gleixner  * we see if any fixup is available for our current hardware. if there
789a163ed8SThomas Gleixner  * is a fixup, we call it and we expect to never return from it. if we
799a163ed8SThomas Gleixner  * do return, we keep looking and then eventually fall back to the
809a163ed8SThomas Gleixner  * standard mach_reboot on return.
819a163ed8SThomas Gleixner  */
mach_reboot_fixups(void)829a163ed8SThomas Gleixner void mach_reboot_fixups(void)
839a163ed8SThomas Gleixner {
84ebdd561aSJan Beulich 	const struct device_fixup *cur;
859a163ed8SThomas Gleixner 	struct pci_dev *dev;
869a163ed8SThomas Gleixner 	int i;
879a163ed8SThomas Gleixner 
883d034aecSDenis V. Lunev 	/* we can be called from sysrq-B code. In such a case it is
893d034aecSDenis V. Lunev 	 * prohibited to dig PCI */
903d034aecSDenis V. Lunev 	if (in_interrupt())
913d034aecSDenis V. Lunev 		return;
923d034aecSDenis V. Lunev 
939a163ed8SThomas Gleixner 	for (i=0; i < ARRAY_SIZE(fixups_table); i++) {
949a163ed8SThomas Gleixner 		cur = &(fixups_table[i]);
959a163ed8SThomas Gleixner 		dev = pci_get_device(cur->vendor, cur->device, NULL);
969a163ed8SThomas Gleixner 		if (!dev)
979a163ed8SThomas Gleixner 			continue;
989a163ed8SThomas Gleixner 
999a163ed8SThomas Gleixner 		cur->reboot_fixup(dev);
10057fea8f7SXiaotian Feng 		pci_dev_put(dev);
1019a163ed8SThomas Gleixner 	}
1029a163ed8SThomas Gleixner }
1039a163ed8SThomas Gleixner 
104