fdt: Add several apis to decode pci device node

This commit adds several APIs to decode PCI device node according to
the Open Firmware PCI bus bindings, including:
- fdtdec_get_pci_addr() for encoded pci address
- fdtdec_get_pci_vendev() for vendor id and device id
- fdtdec_get_pci_bdf() for pci device bdf triplet
- fdtdec_get_pci_bar32() for pci device register bar

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
(Include <pci.h> in fdtdec.h and adjust tegra to fix build error)
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 745b390..487122e 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -126,6 +126,163 @@
 	return fdtdec_get_addr_size(blob, node, prop_name, NULL);
 }
 
+#ifdef CONFIG_PCI
+int fdtdec_get_pci_addr(const void *blob, int node, enum fdt_pci_space type,
+		const char *prop_name, struct fdt_pci_addr *addr)
+{
+	const u32 *cell;
+	int len;
+	int ret = -ENOENT;
+
+	debug("%s: %s: ", __func__, prop_name);
+
+	/*
+	 * If we follow the pci bus bindings strictly, we should check
+	 * the value of the node's parent node's #address-cells and
+	 * #size-cells. They need to be 3 and 2 accordingly. However,
+	 * for simplicity we skip the check here.
+	 */
+	cell = fdt_getprop(blob, node, prop_name, &len);
+	if (!cell)
+		goto fail;
+
+	if ((len % FDT_PCI_REG_SIZE) == 0) {
+		int num = len / FDT_PCI_REG_SIZE;
+		int i;
+
+		for (i = 0; i < num; i++) {
+			debug("pci address #%d: %08lx %08lx %08lx\n", i,
+			      (ulong)fdt_addr_to_cpu(cell[0]),
+			      (ulong)fdt_addr_to_cpu(cell[1]),
+			      (ulong)fdt_addr_to_cpu(cell[2]));
+			if ((fdt_addr_to_cpu(*cell) & type) == type) {
+				addr->phys_hi = fdt_addr_to_cpu(cell[0]);
+				addr->phys_mid = fdt_addr_to_cpu(cell[1]);
+				addr->phys_lo = fdt_addr_to_cpu(cell[2]);
+				break;
+			} else {
+				cell += (FDT_PCI_ADDR_CELLS +
+					 FDT_PCI_SIZE_CELLS);
+			}
+		}
+
+		if (i == num)
+			goto fail;
+
+		return 0;
+	} else {
+		ret = -EINVAL;
+	}
+
+fail:
+	debug("(not found)\n");
+	return ret;
+}
+
+int fdtdec_get_pci_vendev(const void *blob, int node, u16 *vendor, u16 *device)
+{
+	const char *list, *end;
+	int len;
+
+	list = fdt_getprop(blob, node, "compatible", &len);
+	if (!list)
+		return -ENOENT;
+
+	end = list + len;
+	while (list < end) {
+		char *s;
+
+		len = strlen(list);
+		if (len >= strlen("pciVVVV,DDDD")) {
+			s = strstr(list, "pci");
+
+			/*
+			 * check if the string is something like pciVVVV,DDDD.RR
+			 * or just pciVVVV,DDDD
+			 */
+			if (s && s[7] == ',' &&
+			    (s[12] == '.' || s[12] == 0)) {
+				s += 3;
+				*vendor = simple_strtol(s, NULL, 16);
+
+				s += 5;
+				*device = simple_strtol(s, NULL, 16);
+
+				return 0;
+			}
+		} else {
+			list += (len + 1);
+		}
+	}
+
+	return -ENOENT;
+}
+
+int fdtdec_get_pci_bdf(const void *blob, int node,
+		struct fdt_pci_addr *addr, pci_dev_t *bdf)
+{
+	u16 dt_vendor, dt_device, vendor, device;
+	int ret;
+
+	/* get vendor id & device id from the compatible string */
+	ret = fdtdec_get_pci_vendev(blob, node, &dt_vendor, &dt_device);
+	if (ret)
+		return ret;
+
+	/* extract the bdf from fdt_pci_addr */
+	*bdf = addr->phys_hi & 0xffff00;
+
+	/* read vendor id & device id based on bdf */
+	pci_read_config_word(*bdf, PCI_VENDOR_ID, &vendor);
+	pci_read_config_word(*bdf, PCI_DEVICE_ID, &device);
+
+	/*
+	 * Note there are two places in the device tree to fully describe
+	 * a pci device: one is via compatible string with a format of
+	 * "pciVVVV,DDDD" and the other one is the bdf numbers encoded in
+	 * the device node's reg address property. We read the vendor id
+	 * and device id based on bdf and compare the values with the
+	 * "VVVV,DDDD". If they are the same, then we are good to use bdf
+	 * to read device's bar. But if they are different, we have to rely
+	 * on the vendor id and device id extracted from the compatible
+	 * string and locate the real bdf by pci_find_device(). This is
+	 * because normally we may only know device's device number and
+	 * function number when writing device tree. The bus number is
+	 * dynamically assigned during the pci enumeration process.
+	 */
+	if ((dt_vendor != vendor) || (dt_device != device)) {
+		*bdf = pci_find_device(dt_vendor, dt_device, 0);
+		if (*bdf == -1)
+			return -ENODEV;
+	}
+
+	return 0;
+}
+
+int fdtdec_get_pci_bar32(const void *blob, int node,
+		struct fdt_pci_addr *addr, u32 *bar)
+{
+	pci_dev_t bdf;
+	int barnum;
+	int ret;
+
+	/* get pci devices's bdf */
+	ret = fdtdec_get_pci_bdf(blob, node, addr, &bdf);
+	if (ret)
+		return ret;
+
+	/* extract the bar number from fdt_pci_addr */
+	barnum = addr->phys_hi & 0xff;
+	if ((barnum < PCI_BASE_ADDRESS_0) || (barnum > PCI_CARDBUS_CIS))
+		return -EINVAL;
+
+	barnum = (barnum - PCI_BASE_ADDRESS_0) / 4;
+	*bar = pci_read_bar32(pci_bus_to_hose(PCI_BUS(bdf)), bdf, barnum);
+
+	return 0;
+}
+#endif
+
 uint64_t fdtdec_get_uint64(const void *blob, int node, const char *prop_name,
 		uint64_t default_val)
 {
@@ -795,20 +952,6 @@
 	return fdt_get_resource(fdt, node, property, index, res);
 }
 
-int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf)
-{
-	const fdt32_t *prop;
-	int len;
-
-	prop = fdt_getprop(fdt, node, "reg", &len);
-	if (!prop)
-		return len;
-
-	*bdf = fdt32_to_cpu(*prop) & 0xffffff;
-
-	return 0;
-}
-
 int fdtdec_decode_memory_region(const void *blob, int config_node,
 				const char *mem_type, const char *suffix,
 				fdt_addr_t *basep, fdt_size_t *sizep)