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