+/**
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ * PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ *
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+static bool pci_match_one_id(const struct pci_device_id *id,
+ const struct pci_device_id *find)
+{
+ if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) &&
+ (id->device == PCI_ANY_ID || id->device == find->device) &&
+ (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) &&
+ (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) &&
+ !((id->class ^ find->class) & id->class_mask))
+ return true;
+
+ return false;
+}
+
+/**
+ * pci_find_and_bind_driver() - Find and bind the right PCI driver
+ *
+ * This only looks at certain fields in the descriptor.
+ *
+ * @parent: Parent bus
+ * @find_id: Specification of the driver to find
+ * @bdf: Bus/device/function addreess - see PCI_BDF()
+ * @devp: Returns a pointer to the device created
+ * @return 0 if OK, -EPERM if the device is not needed before relocation and
+ * therefore was not created, other -ve value on error
+ */
+static int pci_find_and_bind_driver(struct udevice *parent,
+ struct pci_device_id *find_id,
+ pci_dev_t bdf, struct udevice **devp)
+{
+ struct pci_driver_entry *start, *entry;
+ const char *drv;
+ int n_ents;
+ int ret;
+ char name[30], *str;
+ bool bridge;
+
+ *devp = NULL;
+
+ debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
+ find_id->vendor, find_id->device);
+ start = ll_entry_start(struct pci_driver_entry, pci_driver_entry);
+ n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry);
+ for (entry = start; entry != start + n_ents; entry++) {
+ const struct pci_device_id *id;
+ struct udevice *dev;
+ const struct driver *drv;
+
+ for (id = entry->match;
+ id->vendor || id->subvendor || id->class_mask;
+ id++) {
+ if (!pci_match_one_id(id, find_id))
+ continue;
+
+ drv = entry->driver;
+
+ /*
+ * In the pre-relocation phase, we only bind devices
+ * whose driver has the DM_FLAG_PRE_RELOC set, to save
+ * precious memory space as on some platforms as that
+ * space is pretty limited (ie: using Cache As RAM).
+ */
+ if (!(gd->flags & GD_FLG_RELOC) &&
+ !(drv->flags & DM_FLAG_PRE_RELOC))
+ return -EPERM;
+
+ /*
+ * We could pass the descriptor to the driver as
+ * platdata (instead of NULL) and allow its bind()
+ * method to return -ENOENT if it doesn't support this
+ * device. That way we could continue the search to
+ * find another driver. For now this doesn't seem
+ * necesssary, so just bind the first match.
+ */
+ ret = device_bind(parent, drv, drv->name, NULL, -1,
+ &dev);
+ if (ret)
+ goto error;
+ debug("%s: Match found: %s\n", __func__, drv->name);
+ dev->driver_data = find_id->driver_data;
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ bridge = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI;
+ /*
+ * In the pre-relocation phase, we only bind bridge devices to save
+ * precious memory space as on some platforms as that space is pretty
+ * limited (ie: using Cache As RAM).
+ */
+ if (!(gd->flags & GD_FLG_RELOC) && !bridge)
+ return -EPERM;
+
+ /* Bind a generic driver so that the device can be used */
+ sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(bdf),
+ PCI_FUNC(bdf));
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ drv = bridge ? "pci_bridge_drv" : "pci_generic_drv";
+
+ ret = device_bind_driver(parent, drv, str, devp);
+ if (ret) {
+ debug("%s: Failed to bind generic driver: %d\n", __func__, ret);
+ return ret;
+ }
+ debug("%s: No match found: bound generic driver instead\n", __func__);
+
+ return 0;
+
+error:
+ debug("%s: No match found: error %d\n", __func__, ret);
+ return ret;
+}
+