[U-Boot] [PATCH v2 11/12] dm: pci: Really support binding pci device in the device tree

Bin Meng bmeng.cn at gmail.com
Thu Aug 20 15:40:27 CEST 2015


The dm pci doc says it supports binding pci device which appears
in the device tree. However it is not true, at least on Intel
Crown Bay. Currently the crownbay.dts defines 4 pci uart devices
within the pci bus controller's node. pci_find_and_bind_driver()
only scans U_BOOT_PCI_DEVICE defined driver list and if nothing
is found, it binds devices to bridge driver or generic driver.
Now we change the codes to first scan device tree for any pci
devices listed there, if nothing go on with previous logic.

With this commit, we can get pci uart work on Intel Crown Bay.
The previous 'dm tree' output before this commit is:

 pci         [ + ]    |   |   `-- pci_1:0.0
 pci_generic [   ]    |   |       |-- pci_2:0.0
 ......
 pci_generic [   ]    |   |       |-- pci_2:a.0
 pci_generic [   ]    |   |       |-- pci_2:a,1
 pci_generic [   ]    |   |       |-- pci_2:a,2
 pci_generic [   ]    |   |       |-- pci_2:a,3
 pci_generic [   ]    |   |       |-- pci_2:a,4
 pci_generic [   ]    |   |       |-- pci_2:c.0
 ......

We have device nodes for 2:a,1/2:a,2/2:a,3/2:a,4, now we get:

 pci         [ + ]    |   |   `-- pci_1:0.0
 pci_generic [   ]    |   |       |-- pci_2:0.0
 ......
 pci_generic [   ]    |   |       |-- pci_2:a.0
 serial      [   ]    |   |       |-- uart at a,1
 serial      [   ]    |   |       |-- uart at a,2
 serial      [   ]    |   |       |-- uart at a,3
 serial      [   ]    |   |       |-- uart at a,4
 pci_generic [   ]    |   |       |-- pci_2:c.0
 ......

Signed-off-by: Bin Meng <bmeng.cn at gmail.com>
---

Changes in v2: None

 drivers/pci/pci-uclass.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++
 include/pci.h            |  11 +++++
 2 files changed, 114 insertions(+)

diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 4a509a2..1e4d9be 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -450,6 +450,82 @@ static bool pci_match_one_id(const struct pci_device_id *id,
 	return false;
 }
 
+static int pci_scan_fdt_node(struct udevice *bus,
+			     struct pci_fdt_info **find_info, int *num)
+{
+	struct pci_root_priv *priv;
+	struct pci_fdt_info *fdt_info;
+	const void *fdt = gd->fdt_blob;
+	int offset, root_offset;
+	u16 vendor, device;
+	int i = 0;
+
+	/* get root bus device */
+	do {
+		if (device_get_uclass_id(bus->parent) == UCLASS_ROOT)
+			break;
+		bus = bus->parent;
+	} while (bus);
+
+	priv = bus->priv;
+	if (!priv) {
+		priv = calloc(1, sizeof(struct pci_root_priv));
+		if (!priv)
+			return -ENOMEM;
+		bus->priv = priv;
+	}
+
+	/*
+	 * Scan device tree from root bus node
+	 *
+	 * We scan device tree to check if any pci device is specified
+	 * in the device tree. The device tree will be scanned twice.
+	 * After the first scan we know how many pci device nodes are
+	 * there. For the second scan we save pci device's vendor id &
+	 * device id as well as its device tree node offset.
+	 */
+	if (!priv->fdt_scanned) {
+		root_offset = bus->of_offset;
+
+		/* first scan */
+		for (offset = fdt_next_node(fdt, root_offset, NULL);
+		     offset >= 0;
+		     offset = fdt_next_node(fdt, offset, NULL)) {
+			if (!fdtdec_get_pci_vendev(fdt, offset,
+						   &vendor, &device))
+				priv->fdt_info_entries++;
+		}
+
+		if (priv->fdt_info_entries) {
+			fdt_info = calloc(priv->fdt_info_entries,
+					  sizeof(struct pci_fdt_info));
+			if (!fdt_info)
+				return -ENOMEM;
+			priv->fdt_info = fdt_info;
+
+			/* second scan */
+			for (offset = fdt_next_node(fdt, root_offset, NULL);
+			     offset >= 0;
+			     offset = fdt_next_node(fdt, offset, NULL)) {
+				if (!fdtdec_get_pci_vendev(fdt, offset,
+							   &vendor, &device)) {
+					fdt_info[i].vendor = vendor;
+					fdt_info[i].device = device;
+					fdt_info[i].offset = offset;
+					i++;
+				}
+			}
+		}
+
+		priv->fdt_scanned = true;
+	}
+
+	*find_info = priv->fdt_info;
+	*num = priv->fdt_info_entries;
+
+	return 0;
+}
+
 /**
  * pci_find_and_bind_driver() - Find and bind the right PCI driver
  *
@@ -465,11 +541,33 @@ static int pci_find_and_bind_driver(struct udevice *parent,
 	int ret;
 	char name[30], *str;
 	bool bridge;
+	struct pci_fdt_info *find_info;
+	int i, num;
 
 	*devp = NULL;
 
 	debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__,
 	      find_id->vendor, find_id->device);
+
+	/* Scan device tree to see if any pci device is listed */
+	ret = pci_scan_fdt_node(parent, &find_info, &num);
+	if (ret)
+		goto error;
+	if (find_info) {
+		for (i = 0; i < num; i++) {
+			if (find_id->vendor == find_info->vendor &&
+			    find_id->device == find_info->device) {
+				ret = lists_bind_fdt(parent, gd->fdt_blob,
+						     find_info->offset, devp);
+				if (ret)
+					goto error;
+				return 0;
+			}
+			find_info++;
+		}
+	}
+
+	/* Then check U_BOOT_PCI_DEVICE defined driver list */
 	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++) {
@@ -514,6 +612,11 @@ static int pci_find_and_bind_driver(struct udevice *parent,
 		}
 	}
 
+	/*
+	 * If we don't find any match driver above, bind the device to
+	 * predefined driver - "pci_bridge_drv" or "pci_generic_drv".
+	 */
+
 	bridge = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI;
 	/*
 	 * In the pre-relocation phase, we only bind bridge devices to save
diff --git a/include/pci.h b/include/pci.h
index 488ff44..a2d4ef2 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -512,6 +512,17 @@ struct pci_device_id {
 	unsigned long driver_data;	/* Data private to the driver */
 };
 
+struct pci_fdt_info {
+	unsigned int vendor, device;	/* Vendor and device ID*/
+	unsigned int offset;		/* Device tree offset */
+};
+
+struct pci_root_priv {
+	struct pci_fdt_info *fdt_info;
+	int fdt_info_entries;
+	bool fdt_scanned;
+};
+
 struct pci_controller;
 
 struct pci_config_table {
-- 
1.8.2.1



More information about the U-Boot mailing list