diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
index 9c87d10..abfa429 100644
--- a/drivers/pci/controller/pcie-brcmstb.c
+++ b/drivers/pci/controller/pcie-brcmstb.c
@@ -21,6 +21,7 @@
 #include <linux/printk.h>
 #include <linux/sizes.h>
 #include <linux/slab.h>
+#include <soc/brcmstb/common.h>
 #include <soc/brcmstb/memory_api.h>
 #include <linux/string.h>
 #include <linux/types.h>
@@ -321,6 +322,7 @@ 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)))
 
+static struct of_pci_range *brcm_dma_ranges;
 static phys_addr_t scb_size[BRCM_MAX_SCB];
 static int num_memc;
 static int num_pcie;
@@ -599,6 +601,79 @@ static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie,
 		WR_FLD_RB(pcie->base, PCIE_MISC_PCIE_CTRL, PCIE_PERSTB, !val);
 }
 
+static int brcm_pcie_parse_map_dma_ranges(struct brcm_pcie *pcie)
+{
+	int i;
+	struct of_pci_range_parser parser;
+	struct device_node *dn = pcie->dn;
+
+	/*
+	 * Parse dma-ranges property if present.  If there are multiple
+	 * PCIe controllers, we only have to parse from one of them since
+	 * the others will have an identical mapping.
+	 */
+	if (!of_pci_dma_range_parser_init(&parser, dn)) {
+		struct of_pci_range *p;
+		unsigned int max_ranges = (parser.end - parser.range)
+			/ parser.np;
+
+		/* Add a null entry to indicate the end of the array */
+		brcm_dma_ranges = kcalloc(max_ranges + 1,
+					  sizeof(struct of_pci_range),
+					  GFP_KERNEL);
+		if (!brcm_dma_ranges)
+			return -ENOMEM;
+
+		p = brcm_dma_ranges;
+		while (of_pci_range_parser_one(&parser, p))
+			p++;
+	}
+
+	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);
+			return -EINVAL;
+		} else if (size) {
+			scb_size[i] = roundup_pow_of_two_64(size);
+			num_memc++;
+		} else {
+			break;
+		}
+	}
+
+	return 0;
+}
+
+dma_addr_t brcm_phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+	struct of_pci_range *p;
+
+	if (!dev || !dev_is_pci(dev))
+		return (dma_addr_t)paddr;
+	for (p = brcm_dma_ranges; p && p->size; p++)
+		if (paddr >= p->cpu_addr && paddr < (p->cpu_addr + p->size))
+			return (dma_addr_t)(paddr - p->cpu_addr + p->pci_addr);
+
+	return (dma_addr_t)paddr;
+}
+
+phys_addr_t brcm_dma_to_phys(struct device *dev, dma_addr_t dev_addr)
+{
+	struct of_pci_range *p;
+
+	if (!dev || !dev_is_pci(dev))
+		return (phys_addr_t)dev_addr;
+	for (p = brcm_dma_ranges; p && p->size; p++)
+		if (dev_addr >= p->pci_addr
+		    && dev_addr < (p->pci_addr + p->size))
+			return (phys_addr_t)
+				(dev_addr - p->pci_addr + p->cpu_addr);
+
+	return (phys_addr_t)dev_addr;
+}
+
 static int brcm_pcie_add_controller(struct brcm_pcie *pcie)
 {
 	int i, ret = 0;
@@ -610,6 +685,10 @@ static int brcm_pcie_add_controller(struct brcm_pcie *pcie)
 		goto done;
 	}
 
+	ret = brcm_pcie_parse_map_dma_ranges(pcie);
+	if (ret)
+		goto done;
+
 	/* Determine num_memc and their sizes */
 	for (i = 0, num_memc = 0; i < BRCM_MAX_SCB; i++) {
 		u64 size = brcmstb_memory_memc_size(i);
@@ -639,8 +718,13 @@ static int brcm_pcie_add_controller(struct brcm_pcie *pcie)
 static void brcm_pcie_remove_controller(struct brcm_pcie *pcie)
 {
 	mutex_lock(&brcm_pcie_lock);
-	if (--num_pcie == 0)
-		num_memc = 0;
+	if (--num_pcie > 0)
+		goto out;
+
+	kfree(brcm_dma_ranges);
+	brcm_dma_ranges = NULL;
+	num_memc = 0;
+out:
 	mutex_unlock(&brcm_pcie_lock);
 }
 
@@ -747,11 +831,37 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
 	 */
 	rc_bar2_size = roundup_pow_of_two_64(total_mem_size);
 
-	/*
-	 * Set simple configuration based on memory sizes
-	 * only.  We always start the viewport at address 0.
-	 */
-	rc_bar2_offset = 0;
+	if (brcm_dma_ranges) {
+		/*
+		 * The best-case scenario is to place the inbound
+		 * region in the first 4GB of pcie-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,
@@ -969,7 +1079,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;
 
@@ -986,11 +1095,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;
diff --git a/include/soc/brcmstb/common.h b/include/soc/brcmstb/common.h
index cfb5335..a7f19e0 100644
--- a/include/soc/brcmstb/common.h
+++ b/include/soc/brcmstb/common.h
@@ -12,4 +12,20 @@
 
 bool soc_is_brcmstb(void);
 
+#if defined(CONFIG_PCIE_BRCMSTB)
+dma_addr_t brcm_phys_to_dma(struct device *dev, phys_addr_t paddr);
+phys_addr_t brcm_dma_to_phys(struct device *dev, dma_addr_t dev_addr);
+#else
+static inline dma_addr_t brcm_phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+	return (dma_addr_t)paddr;
+}
+
+static inline phys_addr_t brcm_dma_to_phys(struct device *dev,
+					   dma_addr_t dev_addr)
+{
+	return (phys_addr_t)dev_addr;
+}
+#endif
+
 #endif /* __SOC_BRCMSTB_COMMON_H__ */
