summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLucas Stach <l.stach@pengutronix.de>2018-08-01 10:38:30 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-08-08 09:28:04 +0200
commitfc2c618c2038dda38830d1bfaea7af5b38944f3f (patch)
tree893f24ee71421e1108c0ec2b71eaca625faf1f9f /drivers
parentdcad7abc47f78d401519e64a6d917a2835af26d5 (diff)
downloadbarebox-fc2c618c2038dda38830d1bfaea7af5b38944f3f.tar.gz
barebox-fc2c618c2038dda38830d1bfaea7af5b38944f3f.tar.xz
pci: add quirk infrastructure
This is a cut down version of the Linux kernel PCI quirk infrastructure, which allows to register and execute some fixups before the driver is loaded. Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/pci.c63
1 files changed, 59 insertions, 4 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index bd8b7278ef..d206c53848 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -253,6 +253,8 @@ static void setup_device(struct pci_dev *dev, int max_bar)
}
}
+ pci_fixup_device(pci_fixup_header, dev);
+
pci_write_config_byte(dev, PCI_COMMAND, cmd);
list_add_tail(&dev->bus_list, &dev->bus->devices);
}
@@ -412,6 +414,10 @@ unsigned int pci_scan_bus(struct pci_bus *bus)
class >>= 8;
dev->hdr_type = hdr_type;
+ pci_fixup_device(pci_fixup_early, dev);
+ /* the fixup may have changed the device class */
+ class = dev->class >> 8;
+
pr_debug("class = %08x, hdr_type = %08x\n", class, hdr_type);
pr_debug("%02x:%02x [%04x:%04x]\n", bus->number, dev->devfn,
dev->vendor, dev->device);
@@ -519,12 +525,61 @@ EXPORT_SYMBOL(pci_clear_master);
*/
int pci_enable_device(struct pci_dev *dev)
{
+ int ret;
u32 t;
pci_read_config_dword(dev, PCI_COMMAND, &t);
- return pci_write_config_dword(dev, PCI_COMMAND, t
- | PCI_COMMAND_IO
- | PCI_COMMAND_MEMORY
- );
+ ret = pci_write_config_dword(dev, PCI_COMMAND,
+ t | PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ if (ret)
+ return ret;
+
+ pci_fixup_device(pci_fixup_enable, dev);
+
+ return 0;
}
EXPORT_SYMBOL(pci_enable_device);
+
+static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
+ struct pci_fixup *end)
+{
+ 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)) {
+ f->hook(dev);
+ }
+}
+
+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_enable[];
+extern struct pci_fixup __end_pci_fixups_enable[];
+
+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_enable:
+ start = __start_pci_fixups_enable;
+ end = __end_pci_fixups_enable;
+ break;
+ default:
+ unreachable();
+ }
+ pci_do_fixups(dev, start, end);
+}