18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/pci.h>
38c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
48c2ecf20Sopenharmony_ci#include <linux/timer.h>
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci * These functions are used early on before PCI scanning is done
98c2ecf20Sopenharmony_ci * and all of the pci_dev and pci_bus structures have been created.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_cistatic struct pci_dev *fake_pci_dev(struct pci_channel *hose,
128c2ecf20Sopenharmony_ci	int top_bus, int busnr, int devfn)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	static struct pci_dev dev;
158c2ecf20Sopenharmony_ci	static struct pci_bus bus;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	dev.bus = &bus;
188c2ecf20Sopenharmony_ci	dev.sysdata = hose;
198c2ecf20Sopenharmony_ci	dev.devfn = devfn;
208c2ecf20Sopenharmony_ci	bus.number = busnr;
218c2ecf20Sopenharmony_ci	bus.sysdata = hose;
228c2ecf20Sopenharmony_ci	bus.ops = hose->pci_ops;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if(busnr != top_bus)
258c2ecf20Sopenharmony_ci		/* Fake a parent bus structure. */
268c2ecf20Sopenharmony_ci		bus.parent = &bus;
278c2ecf20Sopenharmony_ci	else
288c2ecf20Sopenharmony_ci		bus.parent = NULL;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return &dev;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define EARLY_PCI_OP(rw, size, type)					\
348c2ecf20Sopenharmony_ciint __init early_##rw##_config_##size(struct pci_channel *hose,		\
358c2ecf20Sopenharmony_ci	int top_bus, int bus, int devfn, int offset, type value)	\
368c2ecf20Sopenharmony_ci{									\
378c2ecf20Sopenharmony_ci	return pci_##rw##_config_##size(				\
388c2ecf20Sopenharmony_ci		fake_pci_dev(hose, top_bus, bus, devfn),		\
398c2ecf20Sopenharmony_ci		offset, value);						\
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciEARLY_PCI_OP(read, byte, u8 *)
438c2ecf20Sopenharmony_ciEARLY_PCI_OP(read, word, u16 *)
448c2ecf20Sopenharmony_ciEARLY_PCI_OP(read, dword, u32 *)
458c2ecf20Sopenharmony_ciEARLY_PCI_OP(write, byte, u8)
468c2ecf20Sopenharmony_ciEARLY_PCI_OP(write, word, u16)
478c2ecf20Sopenharmony_ciEARLY_PCI_OP(write, dword, u32)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciint __init pci_is_66mhz_capable(struct pci_channel *hose,
508c2ecf20Sopenharmony_ci				int top_bus, int current_bus)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	u32 pci_devfn;
538c2ecf20Sopenharmony_ci	unsigned short vid;
548c2ecf20Sopenharmony_ci	int cap66 = -1;
558c2ecf20Sopenharmony_ci	u16 stat;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	pr_info("PCI: Checking 66MHz capabilities...\n");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
608c2ecf20Sopenharmony_ci		if (PCI_FUNC(pci_devfn))
618c2ecf20Sopenharmony_ci			continue;
628c2ecf20Sopenharmony_ci		if (early_read_config_word(hose, top_bus, current_bus,
638c2ecf20Sopenharmony_ci					   pci_devfn, PCI_VENDOR_ID, &vid) !=
648c2ecf20Sopenharmony_ci		    PCIBIOS_SUCCESSFUL)
658c2ecf20Sopenharmony_ci			continue;
668c2ecf20Sopenharmony_ci		if (vid == 0xffff)
678c2ecf20Sopenharmony_ci			continue;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		/* check 66MHz capability */
708c2ecf20Sopenharmony_ci		if (cap66 < 0)
718c2ecf20Sopenharmony_ci			cap66 = 1;
728c2ecf20Sopenharmony_ci		if (cap66) {
738c2ecf20Sopenharmony_ci			early_read_config_word(hose, top_bus, current_bus,
748c2ecf20Sopenharmony_ci					       pci_devfn, PCI_STATUS, &stat);
758c2ecf20Sopenharmony_ci			if (!(stat & PCI_STATUS_66MHZ)) {
768c2ecf20Sopenharmony_ci				printk(KERN_DEBUG
778c2ecf20Sopenharmony_ci				       "PCI: %02x:%02x not 66MHz capable.\n",
788c2ecf20Sopenharmony_ci				       current_bus, pci_devfn);
798c2ecf20Sopenharmony_ci				cap66 = 0;
808c2ecf20Sopenharmony_ci				break;
818c2ecf20Sopenharmony_ci			}
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return cap66 > 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void pcibios_enable_err(struct timer_list *t)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct pci_channel *hose = from_timer(hose, t, err_timer);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	del_timer(&hose->err_timer);
938c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "PCI: re-enabling error IRQ.\n");
948c2ecf20Sopenharmony_ci	enable_irq(hose->err_irq);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void pcibios_enable_serr(struct timer_list *t)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct pci_channel *hose = from_timer(hose, t, serr_timer);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	del_timer(&hose->serr_timer);
1028c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "PCI: re-enabling system error IRQ.\n");
1038c2ecf20Sopenharmony_ci	enable_irq(hose->serr_irq);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid pcibios_enable_timers(struct pci_channel *hose)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	if (hose->err_irq) {
1098c2ecf20Sopenharmony_ci		timer_setup(&hose->err_timer, pcibios_enable_err, 0);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (hose->serr_irq) {
1138c2ecf20Sopenharmony_ci		timer_setup(&hose->serr_timer, pcibios_enable_serr, 0);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * A simple handler for the regular PCI status errors, called from IRQ
1198c2ecf20Sopenharmony_ci * context.
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_ciunsigned int pcibios_handle_status_errors(unsigned long addr,
1228c2ecf20Sopenharmony_ci					  unsigned int status,
1238c2ecf20Sopenharmony_ci					  struct pci_channel *hose)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	unsigned int cmd = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (status & PCI_STATUS_REC_MASTER_ABORT) {
1288c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", addr);
1298c2ecf20Sopenharmony_ci		cmd |= PCI_STATUS_REC_MASTER_ABORT;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (status & PCI_STATUS_REC_TARGET_ABORT) {
1338c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "PCI: target abort: ");
1348c2ecf20Sopenharmony_ci		pcibios_report_status(PCI_STATUS_REC_TARGET_ABORT |
1358c2ecf20Sopenharmony_ci				      PCI_STATUS_SIG_TARGET_ABORT |
1368c2ecf20Sopenharmony_ci				      PCI_STATUS_REC_MASTER_ABORT, 1);
1378c2ecf20Sopenharmony_ci		pr_cont("\n");
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		cmd |= PCI_STATUS_REC_TARGET_ABORT;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (status & (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY)) {
1438c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "PCI: parity error detected: ");
1448c2ecf20Sopenharmony_ci		pcibios_report_status(PCI_STATUS_PARITY |
1458c2ecf20Sopenharmony_ci				      PCI_STATUS_DETECTED_PARITY, 1);
1468c2ecf20Sopenharmony_ci		pr_cont("\n");
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		cmd |= PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		/* Now back off of the IRQ for awhile */
1518c2ecf20Sopenharmony_ci		if (hose->err_irq) {
1528c2ecf20Sopenharmony_ci			disable_irq_nosync(hose->err_irq);
1538c2ecf20Sopenharmony_ci			hose->err_timer.expires = jiffies + HZ;
1548c2ecf20Sopenharmony_ci			add_timer(&hose->err_timer);
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return cmd;
1598c2ecf20Sopenharmony_ci}
160