1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
285b59f5bSPaul Mundt #include <linux/pci.h>
3ef407beeSPaul Mundt #include <linux/interrupt.h>
4ef407beeSPaul Mundt #include <linux/timer.h>
585b59f5bSPaul Mundt #include <linux/kernel.h>
685b59f5bSPaul Mundt
79ad62ec4SPaul Mundt /*
89ad62ec4SPaul Mundt * These functions are used early on before PCI scanning is done
99ad62ec4SPaul Mundt * and all of the pci_dev and pci_bus structures have been created.
109ad62ec4SPaul Mundt */
fake_pci_dev(struct pci_channel * hose,int top_bus,int busnr,int devfn)119ad62ec4SPaul Mundt static struct pci_dev *fake_pci_dev(struct pci_channel *hose,
129ad62ec4SPaul Mundt int top_bus, int busnr, int devfn)
1385b59f5bSPaul Mundt {
149ad62ec4SPaul Mundt static struct pci_dev dev;
159ad62ec4SPaul Mundt static struct pci_bus bus;
1685b59f5bSPaul Mundt
179ad62ec4SPaul Mundt dev.bus = &bus;
189ad62ec4SPaul Mundt dev.sysdata = hose;
199ad62ec4SPaul Mundt dev.devfn = devfn;
209ad62ec4SPaul Mundt bus.number = busnr;
219ad62ec4SPaul Mundt bus.sysdata = hose;
229ad62ec4SPaul Mundt bus.ops = hose->pci_ops;
2385b59f5bSPaul Mundt
249ad62ec4SPaul Mundt if(busnr != top_bus)
2585b59f5bSPaul Mundt /* Fake a parent bus structure. */
269ad62ec4SPaul Mundt bus.parent = &bus;
2785b59f5bSPaul Mundt else
289ad62ec4SPaul Mundt bus.parent = NULL;
2985b59f5bSPaul Mundt
309ad62ec4SPaul Mundt return &dev;
3185b59f5bSPaul Mundt }
3285b59f5bSPaul Mundt
339ad62ec4SPaul Mundt #define EARLY_PCI_OP(rw, size, type) \
349ad62ec4SPaul Mundt int __init early_##rw##_config_##size(struct pci_channel *hose, \
359ad62ec4SPaul Mundt int top_bus, int bus, int devfn, int offset, type value) \
369ad62ec4SPaul Mundt { \
379ad62ec4SPaul Mundt return pci_##rw##_config_##size( \
389ad62ec4SPaul Mundt fake_pci_dev(hose, top_bus, bus, devfn), \
399ad62ec4SPaul Mundt offset, value); \
409ad62ec4SPaul Mundt }
419ad62ec4SPaul Mundt
EARLY_PCI_OP(read,byte,u8 *)429ad62ec4SPaul Mundt EARLY_PCI_OP(read, byte, u8 *)
439ad62ec4SPaul Mundt EARLY_PCI_OP(read, word, u16 *)
449ad62ec4SPaul Mundt EARLY_PCI_OP(read, dword, u32 *)
459ad62ec4SPaul Mundt EARLY_PCI_OP(write, byte, u8)
469ad62ec4SPaul Mundt EARLY_PCI_OP(write, word, u16)
479ad62ec4SPaul Mundt EARLY_PCI_OP(write, dword, u32)
489ad62ec4SPaul Mundt
4985b59f5bSPaul Mundt int __init pci_is_66mhz_capable(struct pci_channel *hose,
5085b59f5bSPaul Mundt int top_bus, int current_bus)
5185b59f5bSPaul Mundt {
5285b59f5bSPaul Mundt u32 pci_devfn;
5385b59f5bSPaul Mundt unsigned short vid;
5485b59f5bSPaul Mundt int cap66 = -1;
5585b59f5bSPaul Mundt u16 stat;
5685b59f5bSPaul Mundt
57601bf18bSGeert Uytterhoeven pr_info("PCI: Checking 66MHz capabilities...\n");
5885b59f5bSPaul Mundt
5985b59f5bSPaul Mundt for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
6085b59f5bSPaul Mundt if (PCI_FUNC(pci_devfn))
6185b59f5bSPaul Mundt continue;
6285b59f5bSPaul Mundt if (early_read_config_word(hose, top_bus, current_bus,
6385b59f5bSPaul Mundt pci_devfn, PCI_VENDOR_ID, &vid) !=
6485b59f5bSPaul Mundt PCIBIOS_SUCCESSFUL)
6585b59f5bSPaul Mundt continue;
6685b59f5bSPaul Mundt if (vid == 0xffff)
6785b59f5bSPaul Mundt continue;
6885b59f5bSPaul Mundt
6985b59f5bSPaul Mundt /* check 66MHz capability */
7085b59f5bSPaul Mundt if (cap66 < 0)
7185b59f5bSPaul Mundt cap66 = 1;
7285b59f5bSPaul Mundt if (cap66) {
7385b59f5bSPaul Mundt early_read_config_word(hose, top_bus, current_bus,
7485b59f5bSPaul Mundt pci_devfn, PCI_STATUS, &stat);
7585b59f5bSPaul Mundt if (!(stat & PCI_STATUS_66MHZ)) {
7685b59f5bSPaul Mundt printk(KERN_DEBUG
7785b59f5bSPaul Mundt "PCI: %02x:%02x not 66MHz capable.\n",
7885b59f5bSPaul Mundt current_bus, pci_devfn);
7985b59f5bSPaul Mundt cap66 = 0;
8085b59f5bSPaul Mundt break;
8185b59f5bSPaul Mundt }
8285b59f5bSPaul Mundt }
8385b59f5bSPaul Mundt }
8485b59f5bSPaul Mundt
8585b59f5bSPaul Mundt return cap66 > 0;
8685b59f5bSPaul Mundt }
87ef407beeSPaul Mundt
pcibios_enable_err(struct timer_list * t)88e99e88a9SKees Cook static void pcibios_enable_err(struct timer_list *t)
89ef407beeSPaul Mundt {
90e99e88a9SKees Cook struct pci_channel *hose = from_timer(hose, t, err_timer);
91ef407beeSPaul Mundt
92ef407beeSPaul Mundt del_timer(&hose->err_timer);
93ef407beeSPaul Mundt printk(KERN_DEBUG "PCI: re-enabling error IRQ.\n");
94ef407beeSPaul Mundt enable_irq(hose->err_irq);
95ef407beeSPaul Mundt }
96ef407beeSPaul Mundt
pcibios_enable_serr(struct timer_list * t)97e99e88a9SKees Cook static void pcibios_enable_serr(struct timer_list *t)
98ef407beeSPaul Mundt {
99e99e88a9SKees Cook struct pci_channel *hose = from_timer(hose, t, serr_timer);
100ef407beeSPaul Mundt
101ef407beeSPaul Mundt del_timer(&hose->serr_timer);
102ef407beeSPaul Mundt printk(KERN_DEBUG "PCI: re-enabling system error IRQ.\n");
103ef407beeSPaul Mundt enable_irq(hose->serr_irq);
104ef407beeSPaul Mundt }
105ef407beeSPaul Mundt
pcibios_enable_timers(struct pci_channel * hose)106ef407beeSPaul Mundt void pcibios_enable_timers(struct pci_channel *hose)
107ef407beeSPaul Mundt {
108ef407beeSPaul Mundt if (hose->err_irq) {
109e99e88a9SKees Cook timer_setup(&hose->err_timer, pcibios_enable_err, 0);
110ef407beeSPaul Mundt }
111ef407beeSPaul Mundt
112ef407beeSPaul Mundt if (hose->serr_irq) {
113e99e88a9SKees Cook timer_setup(&hose->serr_timer, pcibios_enable_serr, 0);
114ef407beeSPaul Mundt }
115ef407beeSPaul Mundt }
116ef407beeSPaul Mundt
117ef407beeSPaul Mundt /*
118ef407beeSPaul Mundt * A simple handler for the regular PCI status errors, called from IRQ
119ef407beeSPaul Mundt * context.
120ef407beeSPaul Mundt */
pcibios_handle_status_errors(unsigned long addr,unsigned int status,struct pci_channel * hose)121ef407beeSPaul Mundt unsigned int pcibios_handle_status_errors(unsigned long addr,
122ef407beeSPaul Mundt unsigned int status,
123ef407beeSPaul Mundt struct pci_channel *hose)
124ef407beeSPaul Mundt {
125ef407beeSPaul Mundt unsigned int cmd = 0;
126ef407beeSPaul Mundt
127ef407beeSPaul Mundt if (status & PCI_STATUS_REC_MASTER_ABORT) {
128ef407beeSPaul Mundt printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", addr);
129ef407beeSPaul Mundt cmd |= PCI_STATUS_REC_MASTER_ABORT;
130ef407beeSPaul Mundt }
131ef407beeSPaul Mundt
132ef407beeSPaul Mundt if (status & PCI_STATUS_REC_TARGET_ABORT) {
133ef407beeSPaul Mundt printk(KERN_DEBUG "PCI: target abort: ");
134ef407beeSPaul Mundt pcibios_report_status(PCI_STATUS_REC_TARGET_ABORT |
135ef407beeSPaul Mundt PCI_STATUS_SIG_TARGET_ABORT |
136ef407beeSPaul Mundt PCI_STATUS_REC_MASTER_ABORT, 1);
137601bf18bSGeert Uytterhoeven pr_cont("\n");
138ef407beeSPaul Mundt
139ef407beeSPaul Mundt cmd |= PCI_STATUS_REC_TARGET_ABORT;
140ef407beeSPaul Mundt }
141ef407beeSPaul Mundt
142ef407beeSPaul Mundt if (status & (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY)) {
143ef407beeSPaul Mundt printk(KERN_DEBUG "PCI: parity error detected: ");
144ef407beeSPaul Mundt pcibios_report_status(PCI_STATUS_PARITY |
145ef407beeSPaul Mundt PCI_STATUS_DETECTED_PARITY, 1);
146601bf18bSGeert Uytterhoeven pr_cont("\n");
147ef407beeSPaul Mundt
148ef407beeSPaul Mundt cmd |= PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY;
149ef407beeSPaul Mundt
150ef407beeSPaul Mundt /* Now back off of the IRQ for awhile */
151ef407beeSPaul Mundt if (hose->err_irq) {
1529ad62ec4SPaul Mundt disable_irq_nosync(hose->err_irq);
153ef407beeSPaul Mundt hose->err_timer.expires = jiffies + HZ;
154ef407beeSPaul Mundt add_timer(&hose->err_timer);
155ef407beeSPaul Mundt }
156ef407beeSPaul Mundt }
157ef407beeSPaul Mundt
158ef407beeSPaul Mundt return cmd;
159ef407beeSPaul Mundt }
160