mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-25 21:24:21 +08:00
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)
This commit is contained in:
parent
949dbc12db
commit
a62e84d7b1
@ -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;
|
||||
|
109
include/fdtdec.h
109
include/fdtdec.h
@ -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.
|
||||
*
|
||||
|
171
lib/fdtdec.c
171
lib/fdtdec.c
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user