diff options
Diffstat (limited to 'drivers/pci/of_property.c')
| -rw-r--r-- | drivers/pci/of_property.c | 115 | 
1 files changed, 111 insertions, 4 deletions
| diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index 58fbafac7c6a..506fcd507113 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -54,9 +54,13 @@ enum of_pci_prop_compatible {  static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,  			       u32 reg_num, u32 flags, bool reloc)  { -	prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) | -		FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) | -		FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn)); +	if (pdev) { +		prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) | +			  FIELD_PREP(OF_PCI_ADDR_FIELD_DEV, PCI_SLOT(pdev->devfn)) | +			  FIELD_PREP(OF_PCI_ADDR_FIELD_FUNC, PCI_FUNC(pdev->devfn)); +	} else +		prop[0] = 0; +  	prop[0] |= flags | reg_num;  	if (!reloc) {  		prop[0] |= OF_PCI_ADDR_FIELD_NONRELOC; @@ -65,7 +69,7 @@ static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,  	}  } -static int of_pci_get_addr_flags(struct resource *res, u32 *flags) +static int of_pci_get_addr_flags(const struct resource *res, u32 *flags)  {  	u32 ss; @@ -390,3 +394,106 @@ int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,  	return 0;  } + +static bool of_pci_is_range_resource(const struct resource *res, u32 *flags) +{ +	if (!(resource_type(res) & IORESOURCE_MEM) && +	    !(resource_type(res) & IORESOURCE_MEM_64)) +		return false; + +	if (of_pci_get_addr_flags(res, flags)) +		return false; + +	return true; +} + +static int of_pci_host_bridge_prop_ranges(struct pci_host_bridge *bridge, +					  struct of_changeset *ocs, +					  struct device_node *np) +{ +	struct resource_entry *window; +	unsigned int ranges_sz = 0; +	unsigned int n_range = 0; +	struct resource *res; +	int n_addr_cells; +	u32 *ranges; +	u64 val64; +	u32 flags; +	int ret; + +	n_addr_cells = of_n_addr_cells(np); +	if (n_addr_cells <= 0 || n_addr_cells > 2) +		return -EINVAL; + +	resource_list_for_each_entry(window, &bridge->windows) { +		res = window->res; +		if (!of_pci_is_range_resource(res, &flags)) +			continue; +		n_range++; +	} + +	if (!n_range) +		return 0; + +	ranges = kcalloc(n_range, +			 (OF_PCI_ADDRESS_CELLS + OF_PCI_SIZE_CELLS + +			  n_addr_cells) * sizeof(*ranges), +			 GFP_KERNEL); +	if (!ranges) +		return -ENOMEM; + +	resource_list_for_each_entry(window, &bridge->windows) { +		res = window->res; +		if (!of_pci_is_range_resource(res, &flags)) +			continue; + +		/* PCI bus address */ +		val64 = res->start; +		of_pci_set_address(NULL, &ranges[ranges_sz], +				   val64 - window->offset, 0, flags, false); +		ranges_sz += OF_PCI_ADDRESS_CELLS; + +		/* Host bus address */ +		if (n_addr_cells == 2) +			ranges[ranges_sz++] = upper_32_bits(val64); +		ranges[ranges_sz++] = lower_32_bits(val64); + +		/* Size */ +		val64 = resource_size(res); +		ranges[ranges_sz] = upper_32_bits(val64); +		ranges[ranges_sz + 1] = lower_32_bits(val64); +		ranges_sz += OF_PCI_SIZE_CELLS; +	} + +	ret = of_changeset_add_prop_u32_array(ocs, np, "ranges", ranges, +					      ranges_sz); +	kfree(ranges); +	return ret; +} + +int of_pci_add_host_bridge_properties(struct pci_host_bridge *bridge, +				      struct of_changeset *ocs, +				      struct device_node *np) +{ +	int ret; + +	ret = of_changeset_add_prop_string(ocs, np, "device_type", "pci"); +	if (ret) +		return ret; + +	ret = of_changeset_add_prop_u32(ocs, np, "#address-cells", +					OF_PCI_ADDRESS_CELLS); +	if (ret) +		return ret; + +	ret = of_changeset_add_prop_u32(ocs, np, "#size-cells", +					OF_PCI_SIZE_CELLS); +	if (ret) +		return ret; + +	ret = of_pci_host_bridge_prop_ranges(bridge, ocs, np); +	if (ret) +		return ret; + +	return 0; +} | 
