]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
85b59f5b | 2 | #include <linux/pci.h> |
ef407bee PM |
3 | #include <linux/interrupt.h> |
4 | #include <linux/timer.h> | |
85b59f5b PM |
5 | #include <linux/kernel.h> |
6 | ||
9ad62ec4 PM |
7 | /* |
8 | * These functions are used early on before PCI scanning is done | |
9 | * and all of the pci_dev and pci_bus structures have been created. | |
10 | */ | |
11 | static struct pci_dev *fake_pci_dev(struct pci_channel *hose, | |
12 | int top_bus, int busnr, int devfn) | |
85b59f5b | 13 | { |
9ad62ec4 PM |
14 | static struct pci_dev dev; |
15 | static struct pci_bus bus; | |
85b59f5b | 16 | |
9ad62ec4 PM |
17 | dev.bus = &bus; |
18 | dev.sysdata = hose; | |
19 | dev.devfn = devfn; | |
20 | bus.number = busnr; | |
21 | bus.sysdata = hose; | |
22 | bus.ops = hose->pci_ops; | |
85b59f5b | 23 | |
9ad62ec4 | 24 | if(busnr != top_bus) |
85b59f5b | 25 | /* Fake a parent bus structure. */ |
9ad62ec4 | 26 | bus.parent = &bus; |
85b59f5b | 27 | else |
9ad62ec4 | 28 | bus.parent = NULL; |
85b59f5b | 29 | |
9ad62ec4 | 30 | return &dev; |
85b59f5b PM |
31 | } |
32 | ||
9ad62ec4 PM |
33 | #define EARLY_PCI_OP(rw, size, type) \ |
34 | int __init early_##rw##_config_##size(struct pci_channel *hose, \ | |
35 | int top_bus, int bus, int devfn, int offset, type value) \ | |
36 | { \ | |
37 | return pci_##rw##_config_##size( \ | |
38 | fake_pci_dev(hose, top_bus, bus, devfn), \ | |
39 | offset, value); \ | |
40 | } | |
41 | ||
42 | EARLY_PCI_OP(read, byte, u8 *) | |
43 | EARLY_PCI_OP(read, word, u16 *) | |
44 | EARLY_PCI_OP(read, dword, u32 *) | |
45 | EARLY_PCI_OP(write, byte, u8) | |
46 | EARLY_PCI_OP(write, word, u16) | |
47 | EARLY_PCI_OP(write, dword, u32) | |
48 | ||
85b59f5b PM |
49 | int __init pci_is_66mhz_capable(struct pci_channel *hose, |
50 | int top_bus, int current_bus) | |
51 | { | |
52 | u32 pci_devfn; | |
53 | unsigned short vid; | |
54 | int cap66 = -1; | |
55 | u16 stat; | |
56 | ||
57 | printk(KERN_INFO "PCI: Checking 66MHz capabilities...\n"); | |
58 | ||
59 | for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { | |
60 | if (PCI_FUNC(pci_devfn)) | |
61 | continue; | |
62 | if (early_read_config_word(hose, top_bus, current_bus, | |
63 | pci_devfn, PCI_VENDOR_ID, &vid) != | |
64 | PCIBIOS_SUCCESSFUL) | |
65 | continue; | |
66 | if (vid == 0xffff) | |
67 | continue; | |
68 | ||
69 | /* check 66MHz capability */ | |
70 | if (cap66 < 0) | |
71 | cap66 = 1; | |
72 | if (cap66) { | |
73 | early_read_config_word(hose, top_bus, current_bus, | |
74 | pci_devfn, PCI_STATUS, &stat); | |
75 | if (!(stat & PCI_STATUS_66MHZ)) { | |
76 | printk(KERN_DEBUG | |
77 | "PCI: %02x:%02x not 66MHz capable.\n", | |
78 | current_bus, pci_devfn); | |
79 | cap66 = 0; | |
80 | break; | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
85 | return cap66 > 0; | |
86 | } | |
ef407bee PM |
87 | |
88 | static void pcibios_enable_err(unsigned long __data) | |
89 | { | |
90 | struct pci_channel *hose = (struct pci_channel *)__data; | |
91 | ||
92 | del_timer(&hose->err_timer); | |
93 | printk(KERN_DEBUG "PCI: re-enabling error IRQ.\n"); | |
94 | enable_irq(hose->err_irq); | |
95 | } | |
96 | ||
97 | static void pcibios_enable_serr(unsigned long __data) | |
98 | { | |
99 | struct pci_channel *hose = (struct pci_channel *)__data; | |
100 | ||
101 | del_timer(&hose->serr_timer); | |
102 | printk(KERN_DEBUG "PCI: re-enabling system error IRQ.\n"); | |
103 | enable_irq(hose->serr_irq); | |
104 | } | |
105 | ||
106 | void pcibios_enable_timers(struct pci_channel *hose) | |
107 | { | |
108 | if (hose->err_irq) { | |
109 | init_timer(&hose->err_timer); | |
110 | hose->err_timer.data = (unsigned long)hose; | |
111 | hose->err_timer.function = pcibios_enable_err; | |
112 | } | |
113 | ||
114 | if (hose->serr_irq) { | |
115 | init_timer(&hose->serr_timer); | |
116 | hose->serr_timer.data = (unsigned long)hose; | |
117 | hose->serr_timer.function = pcibios_enable_serr; | |
118 | } | |
119 | } | |
120 | ||
121 | /* | |
122 | * A simple handler for the regular PCI status errors, called from IRQ | |
123 | * context. | |
124 | */ | |
125 | unsigned int pcibios_handle_status_errors(unsigned long addr, | |
126 | unsigned int status, | |
127 | struct pci_channel *hose) | |
128 | { | |
129 | unsigned int cmd = 0; | |
130 | ||
131 | if (status & PCI_STATUS_REC_MASTER_ABORT) { | |
132 | printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", addr); | |
133 | cmd |= PCI_STATUS_REC_MASTER_ABORT; | |
134 | } | |
135 | ||
136 | if (status & PCI_STATUS_REC_TARGET_ABORT) { | |
137 | printk(KERN_DEBUG "PCI: target abort: "); | |
138 | pcibios_report_status(PCI_STATUS_REC_TARGET_ABORT | | |
139 | PCI_STATUS_SIG_TARGET_ABORT | | |
140 | PCI_STATUS_REC_MASTER_ABORT, 1); | |
141 | printk("\n"); | |
142 | ||
143 | cmd |= PCI_STATUS_REC_TARGET_ABORT; | |
144 | } | |
145 | ||
146 | if (status & (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY)) { | |
147 | printk(KERN_DEBUG "PCI: parity error detected: "); | |
148 | pcibios_report_status(PCI_STATUS_PARITY | | |
149 | PCI_STATUS_DETECTED_PARITY, 1); | |
150 | printk("\n"); | |
151 | ||
152 | cmd |= PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY; | |
153 | ||
154 | /* Now back off of the IRQ for awhile */ | |
155 | if (hose->err_irq) { | |
9ad62ec4 | 156 | disable_irq_nosync(hose->err_irq); |
ef407bee PM |
157 | hose->err_timer.expires = jiffies + HZ; |
158 | add_timer(&hose->err_timer); | |
159 | } | |
160 | } | |
161 | ||
162 | return cmd; | |
163 | } |