fdt: Add several apis to decode pci device node
authorBin Meng <bmeng.cn@gmail.com>
Wed, 31 Dec 2014 08:05:11 +0000 (16:05 +0800)
committerSimon Glass <sjg@chromium.org>
Tue, 13 Jan 2015 15:24:40 +0000 (07:24 -0800)
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)

drivers/pci/pci_tegra.c
include/fdtdec.h
lib/fdtdec.c

index a03ad5ff1fb6b7cf3f55c3c861367707012426cf..f9e05add19157ee885e4c97b7fe4effe052e5333 100644 (file)
@@ -458,6 +458,7 @@ static int tegra_pcie_parse_port_info(const void *fdt, int node,
                                      unsigned int *index,
                                      unsigned int *lanes)
 {
+       struct fdt_pci_addr addr;
        pci_dev_t bdf;
        int err;
 
@@ -469,7 +470,7 @@ static int tegra_pcie_parse_port_info(const void *fdt, int node,
 
        *lanes = err;
 
-       err = fdtdec_pci_get_bdf(fdt, node, &bdf);
+       err = fdtdec_get_pci_bdf(fdt, node, &addr, &bdf);
        if (err < 0) {
                error("failed to parse \"reg\" property");
                return err;
index 5effa240afef3e8a84a2c166b51ee3427bc58c4e..75af750ee576174a1e135952cae09e6ed4f3e1ca 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <libfdt.h>
+#include <pci.h>
 
 /*
  * A typedef for a physical address. Note that fdt data is always big
@@ -50,6 +51,49 @@ struct fdt_resource {
        fdt_addr_t end;
 };
 
+enum fdt_pci_space {
+       FDT_PCI_SPACE_CONFIG = 0,
+       FDT_PCI_SPACE_IO = 0x01000000,
+       FDT_PCI_SPACE_MEM32 = 0x02000000,
+       FDT_PCI_SPACE_MEM64 = 0x03000000,
+       FDT_PCI_SPACE_MEM32_PREF = 0x42000000,
+       FDT_PCI_SPACE_MEM64_PREF = 0x43000000,
+};
+
+#define FDT_PCI_ADDR_CELLS     3
+#define FDT_PCI_SIZE_CELLS     2
+#define FDT_PCI_REG_SIZE       \
+       ((FDT_PCI_ADDR_CELLS + FDT_PCI_SIZE_CELLS) * sizeof(u32))
+
+/*
+ * The Open Firmware spec defines PCI physical address as follows:
+ *
+ *          bits# 31 .... 24 23 .... 16 15 .... 08 07 .... 00
+ *
+ * phys.hi  cell:  npt000ss   bbbbbbbb   dddddfff   rrrrrrrr
+ * phys.mid cell:  hhhhhhhh   hhhhhhhh   hhhhhhhh   hhhhhhhh
+ * phys.lo  cell:  llllllll   llllllll   llllllll   llllllll
+ *
+ * where:
+ *
+ * n:        is 0 if the address is relocatable, 1 otherwise
+ * p:        is 1 if addressable region is prefetchable, 0 otherwise
+ * t:        is 1 if the address is aliased (for non-relocatable I/O) below 1MB
+ *           (for Memory), or below 64KB (for relocatable I/O)
+ * ss:       is the space code, denoting the address space
+ * bbbbbbbb: is the 8-bit Bus Number
+ * ddddd:    is the 5-bit Device Number
+ * fff:      is the 3-bit Function Number
+ * rrrrrrrr: is the 8-bit Register Number
+ * hhhhhhhh: is a 32-bit unsigned number
+ * llllllll: is a 32-bit unsigned number
+ */
+struct fdt_pci_addr {
+       u32     phys_hi;
+       u32     phys_mid;
+       u32     phys_lo;
+};
+
 /**
  * Compute the size of a resource.
  *
@@ -257,6 +301,60 @@ fdt_addr_t fdtdec_get_addr(const void *blob, int node,
 fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
                const char *prop_name, fdt_size_t *sizep);
 
+/**
+ * Look at an address property in a node and return the pci address which
+ * corresponds to the given type in the form of fdt_pci_addr.
+ * The property must hold one fdt_pci_addr with a lengh.
+ *
+ * @param blob         FDT blob
+ * @param node         node to examine
+ * @param type         pci address type (FDT_PCI_SPACE_xxx)
+ * @param prop_name    name of property to find
+ * @param addr         returns pci address in the form of fdt_pci_addr
+ * @return 0 if ok, negative on error
+ */
+int fdtdec_get_pci_addr(const void *blob, int node, enum fdt_pci_space type,
+               const char *prop_name, struct fdt_pci_addr *addr);
+
+/**
+ * Look at the compatible property of a device node that represents a PCI
+ * device and extract pci vendor id and device id from it.
+ *
+ * @param blob         FDT blob
+ * @param node         node to examine
+ * @param vendor       vendor id of the pci device
+ * @param device       device id of the pci device
+ * @return 0 if ok, negative on error
+ */
+int fdtdec_get_pci_vendev(const void *blob, int node,
+               u16 *vendor, u16 *device);
+
+/**
+ * Look at the pci address of a device node that represents a PCI device
+ * and parse the bus, device and function number from it.
+ *
+ * @param blob         FDT blob
+ * @param node         node to examine
+ * @param addr         pci address in the form of fdt_pci_addr
+ * @param bdf          returns bus, device, function triplet
+ * @return 0 if ok, negative on error
+ */
+int fdtdec_get_pci_bdf(const void *blob, int node,
+               struct fdt_pci_addr *addr, pci_dev_t *bdf);
+
+/**
+ * Look at the pci address of a device node that represents a PCI device
+ * and return base address of the pci device's registers.
+ *
+ * @param blob         FDT blob
+ * @param node         node to examine
+ * @param addr         pci address in the form of fdt_pci_addr
+ * @param bar          returns base address of the pci device's registers
+ * @return 0 if ok, negative on error
+ */
+int fdtdec_get_pci_bar32(const void *blob, int node,
+               struct fdt_pci_addr *addr, u32 *bar);
+
 /**
  * Look up a 32-bit integer property in a node and return it. The property
  * must have at least 4 bytes of data. The value of the first cell is
@@ -682,17 +780,6 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property,
                           const char *prop_names, const char *name,
                           struct fdt_resource *res);
 
-/**
- * Look at the reg property of a device node that represents a PCI device
- * and parse the bus, device and function number from it.
- *
- * @param fdt          FDT blob
- * @param node         node to examine
- * @param bdf          returns bus, device, function triplet
- * @return 0 if ok, negative on error
- */
-int fdtdec_pci_get_bdf(const void *fdt, int node, int *bdf);
-
 /**
  * Decode a named region within a memory bank of a given type.
  *
index 745b39083672f47497d68f4fd95a63c33b385419..487122eebcf64c9129abb60c91e91d328f207f29 100644 (file)
@@ -126,6 +126,163 @@ fdt_addr_t fdtdec_get_addr(const void *blob, int node,
        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 @@ int fdt_get_named_resource(const void *fdt, int node, const char *property,
        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)