1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This is a good place to put board specific reboot fixups. |
4 | * |
5 | * List of supported fixups: |
6 | * geode-gx1/cs5530a - Jaya Kumar <jayalk@intworks.biz> |
7 | * geode-gx/lx/cs5536 - Andres Salomon <dilinger@debian.org> |
8 | * |
9 | */ |
10 | |
11 | #include <asm/delay.h> |
12 | #include <linux/pci.h> |
13 | #include <linux/interrupt.h> |
14 | #include <asm/reboot_fixups.h> |
15 | #include <asm/msr.h> |
16 | #include <linux/cs5535.h> |
17 | |
18 | static void cs5530a_warm_reset(struct pci_dev *dev) |
19 | { |
20 | /* writing 1 to the reset control register, 0x44 causes the |
21 | cs5530a to perform a system warm reset */ |
22 | pci_write_config_byte(dev, where: 0x44, val: 0x1); |
23 | udelay(50); /* shouldn't get here but be safe and spin-a-while */ |
24 | return; |
25 | } |
26 | |
27 | static void cs5536_warm_reset(struct pci_dev *dev) |
28 | { |
29 | /* writing 1 to the LSB of this MSR causes a hard reset */ |
30 | wrmsrl(MSR_DIVIL_SOFT_RESET, val: 1ULL); |
31 | udelay(50); /* shouldn't get here but be safe and spin a while */ |
32 | } |
33 | |
34 | static void rdc321x_reset(struct pci_dev *dev) |
35 | { |
36 | unsigned i; |
37 | /* Voluntary reset the watchdog timer */ |
38 | outl(value: 0x80003840, port: 0xCF8); |
39 | /* Generate a CPU reset on next tick */ |
40 | i = inl(port: 0xCFC); |
41 | /* Use the minimum timer resolution */ |
42 | i |= 0x1600; |
43 | outl(value: i, port: 0xCFC); |
44 | outb(value: 1, port: 0x92); |
45 | } |
46 | |
47 | static void ce4100_reset(struct pci_dev *dev) |
48 | { |
49 | int i; |
50 | |
51 | for (i = 0; i < 10; i++) { |
52 | outb(value: 0x2, port: 0xcf9); |
53 | udelay(50); |
54 | } |
55 | } |
56 | |
57 | struct device_fixup { |
58 | unsigned int vendor; |
59 | unsigned int device; |
60 | void (*reboot_fixup)(struct pci_dev *); |
61 | }; |
62 | |
63 | /* |
64 | * PCI ids solely used for fixups_table go here |
65 | */ |
66 | #define PCI_DEVICE_ID_INTEL_CE4100 0x0708 |
67 | |
68 | static const struct device_fixup fixups_table[] = { |
69 | { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset }, |
70 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset }, |
71 | { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE, cs5530a_warm_reset }, |
72 | { PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030, rdc321x_reset }, |
73 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100, ce4100_reset }, |
74 | }; |
75 | |
76 | /* |
77 | * we see if any fixup is available for our current hardware. if there |
78 | * is a fixup, we call it and we expect to never return from it. if we |
79 | * do return, we keep looking and then eventually fall back to the |
80 | * standard mach_reboot on return. |
81 | */ |
82 | void mach_reboot_fixups(void) |
83 | { |
84 | const struct device_fixup *cur; |
85 | struct pci_dev *dev; |
86 | int i; |
87 | |
88 | /* we can be called from sysrq-B code. In such a case it is |
89 | * prohibited to dig PCI */ |
90 | if (in_interrupt()) |
91 | return; |
92 | |
93 | for (i=0; i < ARRAY_SIZE(fixups_table); i++) { |
94 | cur = &(fixups_table[i]); |
95 | dev = pci_get_device(vendor: cur->vendor, device: cur->device, NULL); |
96 | if (!dev) |
97 | continue; |
98 | |
99 | cur->reboot_fixup(dev); |
100 | pci_dev_put(dev); |
101 | } |
102 | } |
103 | |
104 | |