[PATCH v3 4/8] PCI: brcmstb: Add dma-range mapping for inbound traffic

Bjorn Helgaas helgaas at kernel.org
Tue Dec 12 14:32:35 PST 2017


On Tue, Nov 14, 2017 at 05:12:08PM -0500, Jim Quinlan wrote:
> The Broadcom STB PCIe host controller is intimately related to the
> memory subsystem.  This close relationship adds complexity to how cpu
> system memory is mapped to PCIe memory.  Ideally, this mapping is an
> identity mapping, or an identity mapping off by a constant.  Not so in
> this case.
> 
> Consider the Broadcom reference board BCM97445LCC_4X8 which has 6 GB
> of system memory.  Here is how the PCIe controller maps the
> system memory to PCIe memory:
> 
>   memc0-a@[        0....3fffffff] <=> pci@[        0....3fffffff]
>   memc0-b@[100000000...13fffffff] <=> pci@[ 40000000....7fffffff]
>   memc1-a@[ 40000000....7fffffff] <=> pci@[ 80000000....bfffffff]
>   memc1-b@[300000000...33fffffff] <=> pci@[ c0000000....ffffffff]
>   memc2-a@[ 80000000....bfffffff] <=> pci@[100000000...13fffffff]
>   memc2-b@[c00000000...c3fffffff] <=> pci@[140000000...17fffffff]
> 
> Although there are some "gaps" that can be added between the
> individual mappings by software, the permutation of memory regions for
> the most part is fixed by HW.  The solution of having something close
> to an identity mapping is not possible.
> 
> The idea behind this HW design is that the same PCIe module can
> act as an RC or EP, and if it acts as an EP it concatenates all
> of system memory into a BAR so anything can be accessed.  Unfortunately,
> when the PCIe block is in the role of an RC it also presents this
> "BAR" to downstream PCIe devices, rather than offering an identity map
> between its system memory and PCIe space.
> 
> Suppose that an endpoint driver allocs some DMA memory.  Suppose this
> memory is located at 0x6000_0000, which is in the middle of memc1-a.
> The driver wants a dma_addr_t value that it can pass on to the EP to
> use.  Without doing any custom mapping, the EP will use this value for
> DMA: the driver will get a dma_addr_t equal to 0x6000_0000.  But this
> won't work; the device needs a dma_addr_t that reflects the PCIe space
> address, namely 0xa000_0000.
> 
> So, essentially the solution to this problem must modify the
> dma_addr_t returned by the DMA routines routines.  There are two
> ways (I know of) of doing this:
> 
> (a) overriding/redefining the dma_to_phys() and phys_to_dma() calls
> that are used by the dma_ops routines.  This is the approach of
> 
> 	arch/mips/cavium-octeon/dma-octeon.c
> 
> In ARM and ARM64 these two routines are defiend in asm/dma-mapping.h
> as static inline functions.
> 
> (b) Subscribe to a notifier that notifies when a device is added to a
> bus.  When this happens, set_dma_ops() can be called for the device.
> This method is mentioned in:
> 
>     http://lxr.free-electrons.com/source/drivers/of/platform.c?v=3.16#L152
> 
> where it says as a comment
> 
>     "In case if platform code need to use own special DMA
>     configuration, it can use Platform bus notifier and
>     handle BUS_NOTIFY_ADD_DEVICE event to fix up DMA
>     configuration."
> 
> Solution (b) is what this commit does.  It uses its own set of
> dma_ops which are wrappers around the arch_dma_ops.  The
> wrappers translate the dma addresses before/after invoking
> the arch_dma_ops, as appropriate.
> 
> Signed-off-by: Jim Quinlan <jim2101024 at gmail.com>
> ---
>  drivers/pci/host/Makefile           |   4 +-
>  drivers/pci/host/pcie-brcmstb-dma.c | 319 ++++++++++++++++++++++++++++++++++++
>  drivers/pci/host/pcie-brcmstb.c     | 139 +++++++++++++++-
>  drivers/pci/host/pcie-brcmstb.h     |  22 +++
>  4 files changed, 474 insertions(+), 10 deletions(-)
>  create mode 100644 drivers/pci/host/pcie-brcmstb-dma.c
>  create mode 100644 drivers/pci/host/pcie-brcmstb.h
> 
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index a8b9923..6b94c9c 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -21,7 +21,9 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
>  obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
>  obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
>  obj-$(CONFIG_VMD) += vmd.o
> -obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
> +
> +obj-$(CONFIG_PCIE_BRCMSTB) += brcmstb-pcie.o
> +brcmstb-pcie-objs := pcie-brcmstb.o pcie-brcmstb-dma.o

I do not like the addition of more files for this driver (we might
have talked about this before, I can't remember).  If we end up with
2, 3, 4 files for every vendor it's going to be a hassle to manage.

It looks like the only caller of the pcie-brcmstb-dma.c is
pcie-brcmstb.c, so for now, at least, I don't see the point of
splitting them.  They can always be split later if necessary.

>  # The following drivers are for devices that use the generic ACPI
>  # pci_root.c driver but don't support standard ECAM config access.
> diff --git a/drivers/pci/host/pcie-brcmstb-dma.c b/drivers/pci/host/pcie-brcmstb-dma.c
> new file mode 100644
> index 0000000..6c397be
> --- /dev/null
> +++ b/drivers/pci/host/pcie-brcmstb-dma.c
> @@ -0,0 +1,319 @@
> +/*
> + * Copyright (C) 2015-2017 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *

Spurious blank comment line.

> + */
> +#include <linux/dma-mapping.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/smp.h>
> +
> +#include "pcie-brcmstb.h"
> +
> +static const struct dma_map_ops *arch_dma_ops;
> +static const struct dma_map_ops *brcm_dma_ops_ptr;
> +
> +static dma_addr_t brcm_to_pci(dma_addr_t addr)
> +{
> +	struct of_pci_range *p;
> +
> +	if (!num_dma_ranges)
> +		return addr;
> +
> +	for (p = dma_ranges; p < &dma_ranges[num_dma_ranges]; p++)
> +		if (addr >= p->cpu_addr && addr < (p->cpu_addr + p->size))
> +			return addr - p->cpu_addr + p->pci_addr;
> +
> +	return addr;
> +}
> +
> +static dma_addr_t brcm_to_cpu(dma_addr_t addr)
> +{
> +	struct of_pci_range *p;
> +
> +	if (!num_dma_ranges)
> +		return addr;
> +
> +	for (p = dma_ranges; p < &dma_ranges[num_dma_ranges]; p++)
> +		if (addr >= p->pci_addr && addr < (p->pci_addr + p->size))
> +			return addr - p->pci_addr + p->cpu_addr;
> +
> +	return addr;
> +}
> +
> +static void *brcm_alloc(struct device *dev, size_t size, dma_addr_t *handle,
> +			gfp_t gfp, unsigned long attrs)
> +{
> +	void *ret;
> +
> +	ret = arch_dma_ops->alloc(dev, size, handle, gfp, attrs);
> +	if (ret)
> +		*handle = brcm_to_pci(*handle);
> +	return ret;
> +}
> +
> +static void brcm_free(struct device *dev, size_t size, void *cpu_addr,
> +		      dma_addr_t handle, unsigned long attrs)
> +{
> +	handle = brcm_to_cpu(handle);
> +	arch_dma_ops->free(dev, size, cpu_addr, handle, attrs);
> +}
> +
> +static int brcm_mmap(struct device *dev, struct vm_area_struct *vma,
> +		     void *cpu_addr, dma_addr_t dma_addr, size_t size,
> +		     unsigned long attrs)
> +{
> +	dma_addr = brcm_to_cpu(dma_addr);
> +	return arch_dma_ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
> +}
> +
> +static int brcm_get_sgtable(struct device *dev, struct sg_table *sgt,
> +			    void *cpu_addr, dma_addr_t handle, size_t size,
> +			    unsigned long attrs)
> +{
> +	handle = brcm_to_cpu(handle);
> +	return arch_dma_ops->get_sgtable(dev, sgt, cpu_addr, handle, size,
> +				       attrs);
> +}
> +
> +static dma_addr_t brcm_map_page(struct device *dev, struct page *page,
> +				unsigned long offset, size_t size,
> +				enum dma_data_direction dir,
> +				unsigned long attrs)
> +{
> +	return brcm_to_pci(arch_dma_ops->map_page(dev, page, offset, size,
> +						  dir, attrs));
> +}
> +
> +static void brcm_unmap_page(struct device *dev, dma_addr_t handle,
> +			    size_t size, enum dma_data_direction dir,
> +			    unsigned long attrs)
> +{
> +	handle = brcm_to_cpu(handle);
> +	arch_dma_ops->unmap_page(dev, handle, size, dir, attrs);
> +}
> +
> +static int brcm_map_sg(struct device *dev, struct scatterlist *sgl,
> +		       int nents, enum dma_data_direction dir,
> +		       unsigned long attrs)
> +{
> +	int i, j;
> +	struct scatterlist *sg;
> +
> +	for_each_sg(sgl, sg, nents, i) {
> +#ifdef CONFIG_NEED_SG_DMA_LENGTH
> +		sg->dma_length = sg->length;
> +#endif
> +		sg->dma_address =
> +			brcm_dma_ops_ptr->map_page(dev, sg_page(sg), sg->offset,
> +						   sg->length, dir, attrs);
> +		if (dma_mapping_error(dev, sg->dma_address))
> +			goto bad_mapping;
> +	}
> +	return nents;
> +
> +bad_mapping:
> +	for_each_sg(sgl, sg, i, j)
> +		brcm_dma_ops_ptr->unmap_page(dev, sg_dma_address(sg),
> +					     sg_dma_len(sg), dir, attrs);
> +	return 0;
> +}
> +
> +static void brcm_unmap_sg(struct device *dev,
> +			  struct scatterlist *sgl, int nents,
> +			  enum dma_data_direction dir,
> +			  unsigned long attrs)
> +{
> +	int i;
> +	struct scatterlist *sg;
> +
> +	for_each_sg(sgl, sg, nents, i)
> +		brcm_dma_ops_ptr->unmap_page(dev, sg_dma_address(sg),
> +					     sg_dma_len(sg), dir, attrs);
> +}
> +
> +static void brcm_sync_single_for_cpu(struct device *dev,
> +				     dma_addr_t handle, size_t size,
> +				     enum dma_data_direction dir)
> +{
> +	handle = brcm_to_cpu(handle);
> +	arch_dma_ops->sync_single_for_cpu(dev, handle, size, dir);
> +}
> +
> +static void brcm_sync_single_for_device(struct device *dev,
> +					dma_addr_t handle, size_t size,
> +					enum dma_data_direction dir)
> +{
> +	handle = brcm_to_cpu(handle);
> +	arch_dma_ops->sync_single_for_device(dev, handle, size, dir);
> +}
> +
> +static dma_addr_t brcm_map_resource(struct device *dev, phys_addr_t phys,
> +				    size_t size,
> +				    enum dma_data_direction dir,
> +				    unsigned long attrs)
> +{
> +	if (arch_dma_ops->map_resource)
> +		return brcm_to_pci(arch_dma_ops->map_resource
> +				   (dev, phys, size, dir, attrs));
> +	return brcm_to_pci((dma_addr_t)phys);
> +}
> +
> +static void brcm_unmap_resource(struct device *dev, dma_addr_t handle,
> +				size_t size, enum dma_data_direction dir,
> +				unsigned long attrs)
> +{
> +	if (arch_dma_ops->unmap_resource)
> +		arch_dma_ops->unmap_resource(dev, brcm_to_cpu(handle), size,
> +					     dir, attrs);
> +}
> +
> +void brcm_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
> +			  int nents, enum dma_data_direction dir)
> +{
> +	struct scatterlist *sg;
> +	int i;
> +
> +	for_each_sg(sgl, sg, nents, i)
> +		brcm_dma_ops_ptr->sync_single_for_cpu(dev, sg_dma_address(sg),
> +						      sg->length, dir);
> +}
> +
> +void brcm_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
> +			     int nents, enum dma_data_direction dir)
> +{
> +	struct scatterlist *sg;
> +	int i;
> +
> +	for_each_sg(sgl, sg, nents, i)
> +		brcm_dma_ops_ptr->sync_single_for_device(dev,
> +							 sg_dma_address(sg),
> +							 sg->length, dir);
> +}
> +
> +static int brcm_mapping_error(struct device *dev, dma_addr_t dma_addr)
> +{
> +	return arch_dma_ops->mapping_error(dev, dma_addr);
> +}
> +
> +static int brcm_dma_supported(struct device *dev, u64 mask)
> +{
> +	if (num_dma_ranges) {
> +		/*
> +		 * It is our translated addresses that the EP will "see", so
> +		 * we check all of the ranges for the largest possible value.
> +		 */
> +		int i;
> +
> +		for (i = 0; i < num_dma_ranges; i++)
> +			if (dma_ranges[i].pci_addr + dma_ranges[i].size - 1
> +			    > mask)
> +				return 0;
> +		return 1;
> +	}
> +
> +	return arch_dma_ops->dma_supported(dev, mask);
> +}
> +
> +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
> +u64 brcm_get_required_mask)(struct device *dev)
> +{
> +	return arch_dma_ops->get_required_mask(dev);
> +}
> +#endif
> +
> +static const struct dma_map_ops brcm_dma_ops = {
> +	.alloc			= brcm_alloc,
> +	.free			= brcm_free,
> +	.mmap			= brcm_mmap,
> +	.get_sgtable		= brcm_get_sgtable,
> +	.map_page		= brcm_map_page,
> +	.unmap_page		= brcm_unmap_page,
> +	.map_sg			= brcm_map_sg,
> +	.unmap_sg		= brcm_unmap_sg,
> +	.map_resource		= brcm_map_resource,
> +	.unmap_resource		= brcm_unmap_resource,
> +	.sync_single_for_cpu	= brcm_sync_single_for_cpu,
> +	.sync_single_for_device	= brcm_sync_single_for_device,
> +	.sync_sg_for_cpu	= brcm_sync_sg_for_cpu,
> +	.sync_sg_for_device	= brcm_sync_sg_for_device,
> +	.mapping_error		= brcm_mapping_error,
> +	.dma_supported		= brcm_dma_supported,
> +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK
> +	.get_required_mask	= brcm_get_required_mask,
> +#endif
> +};
> +
> +static void brcm_set_dma_ops(struct device *dev)
> +{
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_ARM64)) {
> +		/*
> +		 * We are going to invoke get_dma_ops().  That
> +		 * function, at this point in time, invokes
> +		 * get_arch_dma_ops(), and for ARM64 that function
> +		 * returns a pointer to dummy_dma_ops.  So then we'd
> +		 * like to call arch_setup_dma_ops(), but that isn't
> +		 * exported.  Instead, we call of_dma_configure(),
> +		 * which is exported, and this calls
> +		 * arch_setup_dma_ops().  Once we do this the call to
> +		 * get_dma_ops() will work properly because
> +		 * dev->dma_ops will be set.
> +		 */
> +		ret = of_dma_configure(dev, dev->of_node);
> +		if (ret) {
> +			dev_err(dev, "of_dma_configure() failed: %d\n", ret);
> +			return;
> +		}
> +	}
> +
> +	arch_dma_ops = get_dma_ops(dev);
> +	if (!arch_dma_ops) {
> +		dev_err(dev, "failed to get arch_dma_ops\n");
> +		return;
> +	}
> +
> +	set_dma_ops(dev, &brcm_dma_ops);
> +}
> +
> +static int brcmstb_platform_notifier(struct notifier_block *nb,
> +				     unsigned long event, void *__dev)
> +{
> +	struct device *dev = __dev;
> +
> +	brcm_dma_ops_ptr = &brcm_dma_ops;
> +	if (event != BUS_NOTIFY_ADD_DEVICE)
> +		return NOTIFY_DONE;
> +
> +	brcm_set_dma_ops(dev);
> +	return NOTIFY_OK;
> +}
> +
> +static struct notifier_block brcmstb_platform_nb = {
> +	.notifier_call = brcmstb_platform_notifier,
> +};
> +
> +int brcm_register_notifier(void)
> +{
> +	return bus_register_notifier(&pci_bus_type, &brcmstb_platform_nb);
> +}
> +
> +int brcm_unregister_notifier(void)
> +{
> +	return bus_unregister_notifier(&pci_bus_type, &brcmstb_platform_nb);
> +}
> diff --git a/drivers/pci/host/pcie-brcmstb.c b/drivers/pci/host/pcie-brcmstb.c
> index d8a8f7a..5f4c6aa 100644
> --- a/drivers/pci/host/pcie-brcmstb.c
> +++ b/drivers/pci/host/pcie-brcmstb.c
> @@ -35,6 +35,7 @@
>  #include <soc/brcmstb/memory_api.h>
>  #include <linux/string.h>
>  #include <linux/types.h>
> +#include "pcie-brcmstb.h"
>  
>  /* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
>  #define BRCM_PCIE_CAP_REGS				0x00ac
> @@ -332,6 +333,10 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
>  	((val & ~reg##_##field##_MASK) | \
>  	 (reg##_##field##_MASK & (field_val << reg##_##field##_SHIFT)))
>  
> +/* Also used by pci-brcmstb-dma.c */
> +struct of_pci_range *dma_ranges;
> +int num_dma_ranges;

These names are too generic for global symbols.

> +
>  static struct list_head brcm_pcie = LIST_HEAD_INIT(brcm_pcie);
>  static phys_addr_t scb_size[BRCM_MAX_SCB];
>  static int num_memc;
> @@ -642,25 +647,42 @@ static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie,
>  
>  static int brcm_pcie_add_controller(struct brcm_pcie *pcie)
>  {
> +	int ret = 0;
> +
>  	mutex_lock(&brcm_pcie_lock);
> +	if (list_empty(&brcm_pcie)) {
> +		ret = brcm_register_notifier();
> +		if (ret) {
> +			dev_err(pcie->dev,
> +				"failed to register pci bus notifier\n");
> +			goto done;
> +		}
> +	}
>  	list_add_tail(&pcie->list, &brcm_pcie);
> +done:
>  	mutex_unlock(&brcm_pcie_lock);
> -
> -	return 0;
> +	return ret;
>  }
>  
>  static void brcm_pcie_remove_controller(struct brcm_pcie *pcie)
>  {
>  	struct list_head *pos, *q;
>  	struct brcm_pcie *tmp;
> +	static const char *err_msg = "failed to unregister pci bus notifier\n";
>  
>  	mutex_lock(&brcm_pcie_lock);
>  	list_for_each_safe(pos, q, &brcm_pcie) {
>  		tmp = list_entry(pos, struct brcm_pcie, list);
>  		if (tmp == pcie) {
>  			list_del(pos);
> -			if (list_empty(&brcm_pcie))
> +			if (list_empty(&brcm_pcie)) {
> +				if (brcm_unregister_notifier())
> +					dev_err(pcie->dev, err_msg);
> +				kfree(dma_ranges);
> +				dma_ranges = NULL;
> +				num_dma_ranges = 0;
>  				num_memc = 0;
> +			}
>  			break;
>  		}
>  	}
> @@ -712,6 +734,75 @@ static int brcm_pcie_parse_request_of_pci_ranges(struct brcm_pcie *pcie)
>  	return 0;
>  }
>  
> +static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
> +				     struct device_node *node)
> +{
> +	const int na = 3, ns = 2;
> +	int rlen;
> +
> +	parser->node = node;
> +	parser->pna = of_n_addr_cells(node);
> +	parser->np = parser->pna + na + ns;
> +
> +	parser->range = of_get_property(node, "dma-ranges", &rlen);
> +	if (!parser->range)
> +		return -ENOENT;
> +
> +	parser->end = parser->range + rlen / sizeof(__be32);
> +
> +	return 0;
> +}
> +
> +static int brcm_pcie_parse_map_dma_ranges(struct brcm_pcie *pcie)
> +{
> +	int i, ret = 0;
> +	struct of_pci_range_parser parser;
> +	struct device_node *dn = pcie->dn;
> +
> +	mutex_lock(&brcm_pcie_lock);
> +	if (dma_ranges)
> +		goto done;
> +
> +	/*
> +	 * Parse dma-ranges property if present.  If there are multiple
> +	 * PCI controllers, we only have to parse from one of them since
> +	 * the others will have an identical mapping.
> +	 */
> +	if (!pci_dma_range_parser_init(&parser, dn)) {
> +		unsigned int max_ranges
> +			= (parser.end - parser.range) / parser.np;
> +
> +		dma_ranges = kcalloc(max_ranges, sizeof(struct of_pci_range),
> +				     GFP_KERNEL);
> +		if (!dma_ranges) {
> +			ret =  -ENOMEM;
> +			goto done;
> +		}
> +		for (i = 0; of_pci_range_parser_one(&parser, dma_ranges + i);
> +		     i++)
> +			num_dma_ranges++;
> +	}
> +
> +	for (i = 0, num_memc = 0; i < BRCM_MAX_SCB; i++) {
> +		u64 size = brcmstb_memory_memc_size(i);
> +
> +		if (size == (u64)-1) {
> +			dev_err(pcie->dev, "cannot get memc%d size", i);
> +			ret = -EINVAL;
> +			goto done;
> +		} else if (size) {
> +			scb_size[i] = roundup_pow_of_two_64(size);
> +			num_memc++;
> +		} else {
> +			break;
> +		}
> +	}
> +
> +done:
> +	mutex_unlock(&brcm_pcie_lock);
> +	return ret;
> +}
> +
>  static int brcm_pcie_setup(struct brcm_pcie *pcie)
>  {
>  	void __iomem *base = pcie->base;
> @@ -781,6 +872,38 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
>  	 */
>  	rc_bar2_offset = 0;
>  
> +	if (dma_ranges) {
> +		/*
> +		 * The best-case scenario is to place the inbound
> +		 * region in the first 4GB of pci-space, as some
> +		 * legacy devices can only address 32bits.
> +		 * We would also like to put the MSI under 4GB
> +		 * as well, since some devices require a 32bit
> +		 * MSI target address.
> +		 */
> +		if (total_mem_size <= 0xc0000000ULL &&
> +		    rc_bar2_size <= 0x100000000ULL) {
> +			rc_bar2_offset = 0;
> +		} else {
> +			/*
> +			 * The system memory is 4GB or larger so we
> +			 * cannot start the inbound region at location
> +			 * 0 (since we have to allow some space for
> +			 * outbound memory @ 3GB).  So instead we
> +			 * start it at the 1x multiple of its size
> +			 */
> +			rc_bar2_offset = rc_bar2_size;
> +		}
> +
> +	} else {
> +		/*
> +		 * Set simple configuration based on memory sizes
> +		 * only.  We always start the viewport at address 0,
> +		 * and set the MSI target address accordingly.
> +		 */
> +		rc_bar2_offset = 0;
> +	}
> +
>  	tmp = lower_32_bits(rc_bar2_offset);
>  	tmp = INSERT_FIELD(tmp, PCIE_MISC_RC_BAR2_CONFIG_LO, SIZE,
>  			   encode_ibar_size(rc_bar2_size));
> @@ -995,7 +1118,6 @@ static int brcm_pcie_probe(struct platform_device *pdev)
>  	struct brcm_pcie *pcie;
>  	struct resource *res;
>  	void __iomem *base;
> -	u32 tmp;
>  	struct pci_host_bridge *bridge;
>  	struct pci_bus *child;
>  
> @@ -1012,11 +1134,6 @@ static int brcm_pcie_probe(struct platform_device *pdev)
>  		return -EINVAL;
>  	}
>  
> -	if (of_property_read_u32(dn, "dma-ranges", &tmp) == 0) {
> -		dev_err(&pdev->dev, "cannot yet handle dma-ranges\n");
> -		return -EINVAL;
> -	}
> -
>  	data = of_id->data;
>  	pcie->reg_offsets = data->offsets;
>  	pcie->reg_field_info = data->reg_field_info;
> @@ -1059,6 +1176,10 @@ static int brcm_pcie_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	ret = brcm_pcie_parse_map_dma_ranges(pcie);
> +	if (ret)
> +		return ret;
> +
>  	ret = clk_prepare_enable(pcie->clk);
>  	if (ret) {
>  		dev_err(&pdev->dev, "could not enable clock\n");
> diff --git a/drivers/pci/host/pcie-brcmstb.h b/drivers/pci/host/pcie-brcmstb.h
> new file mode 100644
> index 0000000..d7233f0
> --- /dev/null
> +++ b/drivers/pci/host/pcie-brcmstb.h
> @@ -0,0 +1,22 @@
> +#ifndef __BRCMSTB_PCI_H
> +#define __BRCMSTB_PCI_H
> +/*
> + * Copyright (C) 2015 - 2017 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +int brcm_register_notifier(void);
> +int brcm_unregister_notifier(void);
> +
> +extern struct of_pci_range *dma_ranges;
> +extern int num_dma_ranges;
> +
> +#endif /* __BRCMSTB_PCI_H */
> -- 
> 1.9.0.138.g2de3478
> 



More information about the linux-arm-kernel mailing list