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)