|
@@ -30,6 +30,162 @@
|
|
#include <asm/dma.h> /* isa_dma_bridge_buggy */
|
|
#include <asm/dma.h> /* isa_dma_bridge_buggy */
|
|
#include "pci.h"
|
|
#include "pci.h"
|
|
|
|
|
|
|
|
+static ktime_t fixup_debug_start(struct pci_dev *dev,
|
|
|
|
+ void (*fn)(struct pci_dev *dev))
|
|
|
|
+{
|
|
|
|
+ if (initcall_debug)
|
|
|
|
+ pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current));
|
|
|
|
+
|
|
|
|
+ return ktime_get();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
|
|
|
|
+ void (*fn)(struct pci_dev *dev))
|
|
|
|
+{
|
|
|
|
+ ktime_t delta, rettime;
|
|
|
|
+ unsigned long long duration;
|
|
|
|
+
|
|
|
|
+ rettime = ktime_get();
|
|
|
|
+ delta = ktime_sub(rettime, calltime);
|
|
|
|
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
|
|
|
+ if (initcall_debug || duration > 10000)
|
|
|
|
+ pci_info(dev, "%pF took %lld usecs\n", fn, duration);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
|
|
|
|
+ struct pci_fixup *end)
|
|
|
|
+{
|
|
|
|
+ ktime_t calltime;
|
|
|
|
+
|
|
|
|
+ for (; f < end; f++)
|
|
|
|
+ if ((f->class == (u32) (dev->class >> f->class_shift) ||
|
|
|
|
+ f->class == (u32) PCI_ANY_ID) &&
|
|
|
|
+ (f->vendor == dev->vendor ||
|
|
|
|
+ f->vendor == (u16) PCI_ANY_ID) &&
|
|
|
|
+ (f->device == dev->device ||
|
|
|
|
+ f->device == (u16) PCI_ANY_ID)) {
|
|
|
|
+ calltime = fixup_debug_start(dev, f->hook);
|
|
|
|
+ f->hook(dev);
|
|
|
|
+ fixup_debug_report(dev, calltime, f->hook);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_early[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_early[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_header[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_header[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_final[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_final[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_enable[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_enable[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_resume[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_resume[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_resume_early[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_resume_early[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_suspend[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_suspend[];
|
|
|
|
+extern struct pci_fixup __start_pci_fixups_suspend_late[];
|
|
|
|
+extern struct pci_fixup __end_pci_fixups_suspend_late[];
|
|
|
|
+
|
|
|
|
+static bool pci_apply_fixup_final_quirks;
|
|
|
|
+
|
|
|
|
+void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
|
|
|
|
+{
|
|
|
|
+ struct pci_fixup *start, *end;
|
|
|
|
+
|
|
|
|
+ switch (pass) {
|
|
|
|
+ case pci_fixup_early:
|
|
|
|
+ start = __start_pci_fixups_early;
|
|
|
|
+ end = __end_pci_fixups_early;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_header:
|
|
|
|
+ start = __start_pci_fixups_header;
|
|
|
|
+ end = __end_pci_fixups_header;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_final:
|
|
|
|
+ if (!pci_apply_fixup_final_quirks)
|
|
|
|
+ return;
|
|
|
|
+ start = __start_pci_fixups_final;
|
|
|
|
+ end = __end_pci_fixups_final;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_enable:
|
|
|
|
+ start = __start_pci_fixups_enable;
|
|
|
|
+ end = __end_pci_fixups_enable;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_resume:
|
|
|
|
+ start = __start_pci_fixups_resume;
|
|
|
|
+ end = __end_pci_fixups_resume;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_resume_early:
|
|
|
|
+ start = __start_pci_fixups_resume_early;
|
|
|
|
+ end = __end_pci_fixups_resume_early;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_suspend:
|
|
|
|
+ start = __start_pci_fixups_suspend;
|
|
|
|
+ end = __end_pci_fixups_suspend;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case pci_fixup_suspend_late:
|
|
|
|
+ start = __start_pci_fixups_suspend_late;
|
|
|
|
+ end = __end_pci_fixups_suspend_late;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ /* stupid compiler warning, you would think with an enum... */
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ pci_do_fixups(dev, start, end);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(pci_fixup_device);
|
|
|
|
+
|
|
|
|
+static int __init pci_apply_final_quirks(void)
|
|
|
|
+{
|
|
|
|
+ struct pci_dev *dev = NULL;
|
|
|
|
+ u8 cls = 0;
|
|
|
|
+ u8 tmp;
|
|
|
|
+
|
|
|
|
+ if (pci_cache_line_size)
|
|
|
|
+ printk(KERN_DEBUG "PCI: CLS %u bytes\n",
|
|
|
|
+ pci_cache_line_size << 2);
|
|
|
|
+
|
|
|
|
+ pci_apply_fixup_final_quirks = true;
|
|
|
|
+ for_each_pci_dev(dev) {
|
|
|
|
+ pci_fixup_device(pci_fixup_final, dev);
|
|
|
|
+ /*
|
|
|
|
+ * If arch hasn't set it explicitly yet, use the CLS
|
|
|
|
+ * value shared by all PCI devices. If there's a
|
|
|
|
+ * mismatch, fall back to the default value.
|
|
|
|
+ */
|
|
|
|
+ if (!pci_cache_line_size) {
|
|
|
|
+ pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &tmp);
|
|
|
|
+ if (!cls)
|
|
|
|
+ cls = tmp;
|
|
|
|
+ if (!tmp || cls == tmp)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), using %u bytes\n",
|
|
|
|
+ cls << 2, tmp << 2,
|
|
|
|
+ pci_dfl_cache_line_size << 2);
|
|
|
|
+ pci_cache_line_size = pci_dfl_cache_line_size;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!pci_cache_line_size) {
|
|
|
|
+ printk(KERN_DEBUG "PCI: CLS %u bytes, default %u\n",
|
|
|
|
+ cls << 2, pci_dfl_cache_line_size << 2);
|
|
|
|
+ pci_cache_line_size = cls ? cls : pci_dfl_cache_line_size;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+fs_initcall_sync(pci_apply_final_quirks);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Decoding should be disabled for a PCI device during BAR sizing to avoid
|
|
* Decoding should be disabled for a PCI device during BAR sizing to avoid
|
|
* conflict. But doing so may cause problems on host bridge and perhaps other
|
|
* conflict. But doing so may cause problems on host bridge and perhaps other
|
|
@@ -2981,28 +3137,6 @@ static void quirk_intel_ntb(struct pci_dev *dev)
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb);
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
|
|
|
|
|
|
-static ktime_t fixup_debug_start(struct pci_dev *dev,
|
|
|
|
- void (*fn)(struct pci_dev *dev))
|
|
|
|
-{
|
|
|
|
- if (initcall_debug)
|
|
|
|
- pci_info(dev, "calling %pF @ %i\n", fn, task_pid_nr(current));
|
|
|
|
-
|
|
|
|
- return ktime_get();
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
|
|
|
|
- void (*fn)(struct pci_dev *dev))
|
|
|
|
-{
|
|
|
|
- ktime_t delta, rettime;
|
|
|
|
- unsigned long long duration;
|
|
|
|
-
|
|
|
|
- rettime = ktime_get();
|
|
|
|
- delta = ktime_sub(rettime, calltime);
|
|
|
|
- duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
|
|
|
- if (initcall_debug || duration > 10000)
|
|
|
|
- pci_info(dev, "%pF took %lld usecs\n", fn, duration);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Some BIOS implementations leave the Intel GPU interrupts enabled,
|
|
* Some BIOS implementations leave the Intel GPU interrupts enabled,
|
|
* even though no one is handling them (f.e. i915 driver is never loaded).
|
|
* even though no one is handling them (f.e. i915 driver is never loaded).
|
|
@@ -3397,142 +3531,6 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
|
|
quirk_apple_wait_for_thunderbolt);
|
|
quirk_apple_wait_for_thunderbolt);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
|
|
|
|
- struct pci_fixup *end)
|
|
|
|
-{
|
|
|
|
- ktime_t calltime;
|
|
|
|
-
|
|
|
|
- for (; f < end; f++)
|
|
|
|
- if ((f->class == (u32) (dev->class >> f->class_shift) ||
|
|
|
|
- f->class == (u32) PCI_ANY_ID) &&
|
|
|
|
- (f->vendor == dev->vendor ||
|
|
|
|
- f->vendor == (u16) PCI_ANY_ID) &&
|
|
|
|
- (f->device == dev->device ||
|
|
|
|
- f->device == (u16) PCI_ANY_ID)) {
|
|
|
|
- calltime = fixup_debug_start(dev, f->hook);
|
|
|
|
- f->hook(dev);
|
|
|
|
- fixup_debug_report(dev, calltime, f->hook);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_early[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_early[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_header[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_header[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_final[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_final[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_enable[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_enable[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_resume[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_resume[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_resume_early[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_resume_early[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_suspend[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_suspend[];
|
|
|
|
-extern struct pci_fixup __start_pci_fixups_suspend_late[];
|
|
|
|
-extern struct pci_fixup __end_pci_fixups_suspend_late[];
|
|
|
|
-
|
|
|
|
-static bool pci_apply_fixup_final_quirks;
|
|
|
|
-
|
|
|
|
-void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
|
|
|
|
-{
|
|
|
|
- struct pci_fixup *start, *end;
|
|
|
|
-
|
|
|
|
- switch (pass) {
|
|
|
|
- case pci_fixup_early:
|
|
|
|
- start = __start_pci_fixups_early;
|
|
|
|
- end = __end_pci_fixups_early;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_header:
|
|
|
|
- start = __start_pci_fixups_header;
|
|
|
|
- end = __end_pci_fixups_header;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_final:
|
|
|
|
- if (!pci_apply_fixup_final_quirks)
|
|
|
|
- return;
|
|
|
|
- start = __start_pci_fixups_final;
|
|
|
|
- end = __end_pci_fixups_final;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_enable:
|
|
|
|
- start = __start_pci_fixups_enable;
|
|
|
|
- end = __end_pci_fixups_enable;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_resume:
|
|
|
|
- start = __start_pci_fixups_resume;
|
|
|
|
- end = __end_pci_fixups_resume;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_resume_early:
|
|
|
|
- start = __start_pci_fixups_resume_early;
|
|
|
|
- end = __end_pci_fixups_resume_early;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_suspend:
|
|
|
|
- start = __start_pci_fixups_suspend;
|
|
|
|
- end = __end_pci_fixups_suspend;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case pci_fixup_suspend_late:
|
|
|
|
- start = __start_pci_fixups_suspend_late;
|
|
|
|
- end = __end_pci_fixups_suspend_late;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- /* stupid compiler warning, you would think with an enum... */
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- pci_do_fixups(dev, start, end);
|
|
|
|
-}
|
|
|
|
-EXPORT_SYMBOL(pci_fixup_device);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-static int __init pci_apply_final_quirks(void)
|
|
|
|
-{
|
|
|
|
- struct pci_dev *dev = NULL;
|
|
|
|
- u8 cls = 0;
|
|
|
|
- u8 tmp;
|
|
|
|
-
|
|
|
|
- if (pci_cache_line_size)
|
|
|
|
- printk(KERN_DEBUG "PCI: CLS %u bytes\n",
|
|
|
|
- pci_cache_line_size << 2);
|
|
|
|
-
|
|
|
|
- pci_apply_fixup_final_quirks = true;
|
|
|
|
- for_each_pci_dev(dev) {
|
|
|
|
- pci_fixup_device(pci_fixup_final, dev);
|
|
|
|
- /*
|
|
|
|
- * If arch hasn't set it explicitly yet, use the CLS
|
|
|
|
- * value shared by all PCI devices. If there's a
|
|
|
|
- * mismatch, fall back to the default value.
|
|
|
|
- */
|
|
|
|
- if (!pci_cache_line_size) {
|
|
|
|
- pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &tmp);
|
|
|
|
- if (!cls)
|
|
|
|
- cls = tmp;
|
|
|
|
- if (!tmp || cls == tmp)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), using %u bytes\n",
|
|
|
|
- cls << 2, tmp << 2,
|
|
|
|
- pci_dfl_cache_line_size << 2);
|
|
|
|
- pci_cache_line_size = pci_dfl_cache_line_size;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!pci_cache_line_size) {
|
|
|
|
- printk(KERN_DEBUG "PCI: CLS %u bytes, default %u\n",
|
|
|
|
- cls << 2, pci_dfl_cache_line_size << 2);
|
|
|
|
- pci_cache_line_size = cls ? cls : pci_dfl_cache_line_size;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-fs_initcall_sync(pci_apply_final_quirks);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Following are device-specific reset methods which can be used to
|
|
* Following are device-specific reset methods which can be used to
|
|
* reset a single function if other methods (e.g. FLR, PM D0->D3) are
|
|
* reset a single function if other methods (e.g. FLR, PM D0->D3) are
|