162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/pci.h> 362306a36Sopenharmony_ci#include <linux/interrupt.h> 462306a36Sopenharmony_ci#include <linux/timer.h> 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * These functions are used early on before PCI scanning is done 962306a36Sopenharmony_ci * and all of the pci_dev and pci_bus structures have been created. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_cistatic struct pci_dev *fake_pci_dev(struct pci_channel *hose, 1262306a36Sopenharmony_ci int top_bus, int busnr, int devfn) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci static struct pci_dev dev; 1562306a36Sopenharmony_ci static struct pci_bus bus; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci dev.bus = &bus; 1862306a36Sopenharmony_ci dev.sysdata = hose; 1962306a36Sopenharmony_ci dev.devfn = devfn; 2062306a36Sopenharmony_ci bus.number = busnr; 2162306a36Sopenharmony_ci bus.sysdata = hose; 2262306a36Sopenharmony_ci bus.ops = hose->pci_ops; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if(busnr != top_bus) 2562306a36Sopenharmony_ci /* Fake a parent bus structure. */ 2662306a36Sopenharmony_ci bus.parent = &bus; 2762306a36Sopenharmony_ci else 2862306a36Sopenharmony_ci bus.parent = NULL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return &dev; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define EARLY_PCI_OP(rw, size, type) \ 3462306a36Sopenharmony_ciint __init early_##rw##_config_##size(struct pci_channel *hose, \ 3562306a36Sopenharmony_ci int top_bus, int bus, int devfn, int offset, type value) \ 3662306a36Sopenharmony_ci{ \ 3762306a36Sopenharmony_ci return pci_##rw##_config_##size( \ 3862306a36Sopenharmony_ci fake_pci_dev(hose, top_bus, bus, devfn), \ 3962306a36Sopenharmony_ci offset, value); \ 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciEARLY_PCI_OP(read, byte, u8 *) 4362306a36Sopenharmony_ciEARLY_PCI_OP(read, word, u16 *) 4462306a36Sopenharmony_ciEARLY_PCI_OP(read, dword, u32 *) 4562306a36Sopenharmony_ciEARLY_PCI_OP(write, byte, u8) 4662306a36Sopenharmony_ciEARLY_PCI_OP(write, word, u16) 4762306a36Sopenharmony_ciEARLY_PCI_OP(write, dword, u32) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint __init pci_is_66mhz_capable(struct pci_channel *hose, 5062306a36Sopenharmony_ci int top_bus, int current_bus) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 pci_devfn; 5362306a36Sopenharmony_ci unsigned short vid; 5462306a36Sopenharmony_ci int cap66 = -1; 5562306a36Sopenharmony_ci u16 stat; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci pr_info("PCI: Checking 66MHz capabilities...\n"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { 6062306a36Sopenharmony_ci if (PCI_FUNC(pci_devfn)) 6162306a36Sopenharmony_ci continue; 6262306a36Sopenharmony_ci if (early_read_config_word(hose, top_bus, current_bus, 6362306a36Sopenharmony_ci pci_devfn, PCI_VENDOR_ID, &vid) != 6462306a36Sopenharmony_ci PCIBIOS_SUCCESSFUL) 6562306a36Sopenharmony_ci continue; 6662306a36Sopenharmony_ci if (vid == 0xffff) 6762306a36Sopenharmony_ci continue; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* check 66MHz capability */ 7062306a36Sopenharmony_ci if (cap66 < 0) 7162306a36Sopenharmony_ci cap66 = 1; 7262306a36Sopenharmony_ci if (cap66) { 7362306a36Sopenharmony_ci early_read_config_word(hose, top_bus, current_bus, 7462306a36Sopenharmony_ci pci_devfn, PCI_STATUS, &stat); 7562306a36Sopenharmony_ci if (!(stat & PCI_STATUS_66MHZ)) { 7662306a36Sopenharmony_ci printk(KERN_DEBUG 7762306a36Sopenharmony_ci "PCI: %02x:%02x not 66MHz capable.\n", 7862306a36Sopenharmony_ci current_bus, pci_devfn); 7962306a36Sopenharmony_ci cap66 = 0; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return cap66 > 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void pcibios_enable_err(struct timer_list *t) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct pci_channel *hose = from_timer(hose, t, err_timer); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci del_timer(&hose->err_timer); 9362306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: re-enabling error IRQ.\n"); 9462306a36Sopenharmony_ci enable_irq(hose->err_irq); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void pcibios_enable_serr(struct timer_list *t) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct pci_channel *hose = from_timer(hose, t, serr_timer); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci del_timer(&hose->serr_timer); 10262306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: re-enabling system error IRQ.\n"); 10362306a36Sopenharmony_ci enable_irq(hose->serr_irq); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid pcibios_enable_timers(struct pci_channel *hose) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci if (hose->err_irq) { 10962306a36Sopenharmony_ci timer_setup(&hose->err_timer, pcibios_enable_err, 0); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (hose->serr_irq) { 11362306a36Sopenharmony_ci timer_setup(&hose->serr_timer, pcibios_enable_serr, 0); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * A simple handler for the regular PCI status errors, called from IRQ 11962306a36Sopenharmony_ci * context. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ciunsigned int pcibios_handle_status_errors(unsigned long addr, 12262306a36Sopenharmony_ci unsigned int status, 12362306a36Sopenharmony_ci struct pci_channel *hose) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci unsigned int cmd = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (status & PCI_STATUS_REC_MASTER_ABORT) { 12862306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", addr); 12962306a36Sopenharmony_ci cmd |= PCI_STATUS_REC_MASTER_ABORT; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (status & PCI_STATUS_REC_TARGET_ABORT) { 13362306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: target abort: "); 13462306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_REC_TARGET_ABORT | 13562306a36Sopenharmony_ci PCI_STATUS_SIG_TARGET_ABORT | 13662306a36Sopenharmony_ci PCI_STATUS_REC_MASTER_ABORT, 1); 13762306a36Sopenharmony_ci pr_cont("\n"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cmd |= PCI_STATUS_REC_TARGET_ABORT; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (status & (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY)) { 14362306a36Sopenharmony_ci printk(KERN_DEBUG "PCI: parity error detected: "); 14462306a36Sopenharmony_ci pcibios_report_status(PCI_STATUS_PARITY | 14562306a36Sopenharmony_ci PCI_STATUS_DETECTED_PARITY, 1); 14662306a36Sopenharmony_ci pr_cont("\n"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci cmd |= PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Now back off of the IRQ for awhile */ 15162306a36Sopenharmony_ci if (hose->err_irq) { 15262306a36Sopenharmony_ci disable_irq_nosync(hose->err_irq); 15362306a36Sopenharmony_ci hose->err_timer.expires = jiffies + HZ; 15462306a36Sopenharmony_ci add_timer(&hose->err_timer); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return cmd; 15962306a36Sopenharmony_ci} 160