aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch')
-rw-r--r--target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch1411
1 files changed, 1411 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch b/target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch
new file mode 100644
index 0000000000..b44ec4c4e2
--- /dev/null
+++ b/target/linux/bcm27xx/patches-4.19/950-0460-pcie-brcmstb-Changes-for-BCM2711.patch
@@ -0,0 +1,1411 @@
+From 545951be6cabac8b1df85771c44335a0eaaa3c5d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.org>
+Date: Tue, 19 Feb 2019 22:06:59 +0000
+Subject: [PATCH] pcie-brcmstb: Changes for BCM2711
+
+The initial brcmstb PCIe driver - originally taken from the V3(?)
+patch set - has been modified significantly for the BCM2711.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.org>
+---
+ drivers/dma/bcm2835-dma.c | 107 ++++
+ drivers/pci/controller/Makefile | 4 +
+ drivers/pci/controller/pcie-brcmstb-bounce.c | 564 +++++++++++++++++++
+ drivers/pci/controller/pcie-brcmstb-bounce.h | 32 ++
+ drivers/pci/controller/pcie-brcmstb.c | 237 ++++----
+ drivers/soc/bcm/brcmstb/Makefile | 2 +-
+ drivers/soc/bcm/brcmstb/memory.c | 158 ++++++
+ 7 files changed, 996 insertions(+), 108 deletions(-)
+ create mode 100644 drivers/pci/controller/pcie-brcmstb-bounce.c
+ create mode 100644 drivers/pci/controller/pcie-brcmstb-bounce.h
+ create mode 100644 drivers/soc/bcm/brcmstb/memory.c
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -68,6 +68,17 @@ struct bcm2835_dma_cb {
+ uint32_t pad[2];
+ };
+
++struct bcm2838_dma40_scb {
++ uint32_t ti;
++ uint32_t src;
++ uint32_t srci;
++ uint32_t dst;
++ uint32_t dsti;
++ uint32_t len;
++ uint32_t next_cb;
++ uint32_t rsvd;
++};
++
+ struct bcm2835_cb_entry {
+ struct bcm2835_dma_cb *cb;
+ dma_addr_t paddr;
+@@ -185,6 +196,45 @@ struct bcm2835_desc {
+ #define MAX_DMA_LEN SZ_1G
+ #define MAX_LITE_DMA_LEN (SZ_64K - 4)
+
++/* 40-bit DMA support */
++#define BCM2838_DMA40_CS 0x00
++#define BCM2838_DMA40_CB 0x04
++#define BCM2838_DMA40_DEBUG 0x0c
++#define BCM2858_DMA40_TI 0x10
++#define BCM2838_DMA40_SRC 0x14
++#define BCM2838_DMA40_SRCI 0x18
++#define BCM2838_DMA40_DEST 0x1c
++#define BCM2838_DMA40_DESTI 0x20
++#define BCM2838_DMA40_LEN 0x24
++#define BCM2838_DMA40_NEXT_CB 0x28
++#define BCM2838_DMA40_DEBUG2 0x2c
++
++#define BCM2838_DMA40_CS_ACTIVE BIT(0)
++#define BCM2838_DMA40_CS_END BIT(1)
++
++#define BCM2838_DMA40_CS_QOS(x) (((x) & 0x1f) << 16)
++#define BCM2838_DMA40_CS_PANIC_QOS(x) (((x) & 0x1f) << 20)
++#define BCM2838_DMA40_CS_WRITE_WAIT BIT(28)
++
++#define BCM2838_DMA40_BURST_LEN(x) ((((x) - 1) & 0xf) << 8)
++#define BCM2838_DMA40_INC BIT(12)
++#define BCM2838_DMA40_SIZE_128 (2 << 13)
++
++#define BCM2838_DMA40_MEMCPY_QOS \
++ (BCM2838_DMA40_CS_QOS(0x0) | \
++ BCM2838_DMA40_CS_PANIC_QOS(0x0) | \
++ BCM2838_DMA40_CS_WRITE_WAIT)
++
++#define BCM2838_DMA40_MEMCPY_XFER_INFO \
++ (BCM2838_DMA40_SIZE_128 | \
++ BCM2838_DMA40_INC | \
++ BCM2838_DMA40_BURST_LEN(16))
++
++static void __iomem *memcpy_chan;
++static struct bcm2838_dma40_scb *memcpy_scb;
++static dma_addr_t memcpy_scb_dma;
++DEFINE_SPINLOCK(memcpy_lock);
++
+ static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
+ {
+ /* lite and normal channels have different max frame length */
+@@ -868,6 +918,56 @@ static void bcm2835_dma_free(struct bcm2
+ }
+ }
+
++int bcm2838_dma40_memcpy_init(struct device *dev)
++{
++ if (memcpy_scb)
++ return 0;
++
++ memcpy_scb = dma_alloc_coherent(dev, sizeof(*memcpy_scb),
++ &memcpy_scb_dma, GFP_KERNEL);
++
++ if (!memcpy_scb) {
++ pr_err("bcm2838_dma40_memcpy_init failed!\n");
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(bcm2838_dma40_memcpy_init);
++
++void bcm2838_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size)
++{
++ struct bcm2838_dma40_scb *scb = memcpy_scb;
++ unsigned long flags;
++
++ if (!scb) {
++ pr_err("bcm2838_dma40_memcpy not initialised!\n");
++ return;
++ }
++
++ spin_lock_irqsave(&memcpy_lock, flags);
++
++ scb->ti = 0;
++ scb->src = lower_32_bits(src);
++ scb->srci = upper_32_bits(src) | BCM2838_DMA40_MEMCPY_XFER_INFO;
++ scb->dst = lower_32_bits(dst);
++ scb->dsti = upper_32_bits(dst) | BCM2838_DMA40_MEMCPY_XFER_INFO;
++ scb->len = size;
++ scb->next_cb = 0;
++
++ writel((u32)(memcpy_scb_dma >> 5), memcpy_chan + BCM2838_DMA40_CB);
++ writel(BCM2838_DMA40_MEMCPY_QOS + BCM2838_DMA40_CS_ACTIVE,
++ memcpy_chan + BCM2838_DMA40_CS);
++ /* Poll for completion */
++ while (!(readl(memcpy_chan + BCM2838_DMA40_CS) & BCM2838_DMA40_CS_END))
++ cpu_relax();
++
++ writel(BCM2838_DMA40_CS_END, memcpy_chan + BCM2838_DMA40_CS);
++
++ spin_unlock_irqrestore(&memcpy_lock, flags);
++}
++EXPORT_SYMBOL(bcm2838_dma40_memcpy);
++
+ static const struct of_device_id bcm2835_dma_of_match[] = {
+ { .compatible = "brcm,bcm2835-dma", },
+ {},
+@@ -966,6 +1066,13 @@ static int bcm2835_dma_probe(struct plat
+ /* Channel 0 is used by the legacy API */
+ chans_available &= ~BCM2835_DMA_BULK_MASK;
+
++ /* We can't use channels 11-13 yet */
++ chans_available &= ~(BIT(11) | BIT(12) | BIT(13));
++
++ /* Grab channel 14 for the 40-bit DMA memcpy */
++ chans_available &= ~BIT(14);
++ memcpy_chan = BCM2835_DMA_CHANIO(base, 14);
++
+ /* get irqs for each channel that we support */
+ for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+ /* skip masked out channels */
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -29,6 +29,10 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-medi
+ obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
+ obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
+ obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
++ifdef CONFIG_ARM
++obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb-bounce.o
++endif
++
+ obj-$(CONFIG_VMD) += vmd.o
+ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
+ obj-y += dwc/
+--- /dev/null
++++ b/drivers/pci/controller/pcie-brcmstb-bounce.c
+@@ -0,0 +1,564 @@
++/*
++ * This code started out as a version of arch/arm/common/dmabounce.c,
++ * modified to cope with highmem pages. Now it has been changed heavily -
++ * it now preallocates a large block (currently 4MB) and carves it up
++ * sequentially in ring fashion, and DMA is used to copy the data - to the
++ * point where very little of the original remains.
++ *
++ * Copyright (C) 2019 Raspberry Pi (Trading) Ltd.
++ *
++ * Original version by Brad Parker (brad@heeltoe.com)
++ * Re-written by Christopher Hoover <ch@murgatroid.com>
++ * Made generic by Deepak Saxena <dsaxena@plexity.net>
++ *
++ * Copyright (C) 2002 Hewlett Packard Company.
++ * Copyright (C) 2004 MontaVista Software, Inc.
++ *
++ * 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.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/page-flags.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmapool.h>
++#include <linux/list.h>
++#include <linux/scatterlist.h>
++#include <linux/bitmap.h>
++
++#include <asm/cacheflush.h>
++#include <asm/dma-iommu.h>
++
++#define STATS
++
++#ifdef STATS
++#define DO_STATS(X) do { X ; } while (0)
++#else
++#define DO_STATS(X) do { } while (0)
++#endif
++
++/* ************************************************** */
++
++struct safe_buffer {
++ struct list_head node;
++
++ /* original request */
++ size_t size;
++ int direction;
++
++ struct dmabounce_pool *pool;
++ void *safe;
++ dma_addr_t unsafe_dma_addr;
++ dma_addr_t safe_dma_addr;
++};
++
++struct dmabounce_pool {
++ unsigned long pages;
++ void *virt_addr;
++ dma_addr_t dma_addr;
++ unsigned long *alloc_map;
++ unsigned long alloc_pos;
++ spinlock_t lock;
++ struct device *dev;
++ unsigned long num_pages;
++#ifdef STATS
++ size_t max_size;
++ unsigned long num_bufs;
++ unsigned long max_bufs;
++ unsigned long max_pages;
++#endif
++};
++
++struct dmabounce_device_info {
++ struct device *dev;
++ dma_addr_t threshold;
++ struct list_head safe_buffers;
++ struct dmabounce_pool pool;
++ rwlock_t lock;
++#ifdef STATS
++ unsigned long map_count;
++ unsigned long unmap_count;
++ unsigned long sync_dev_count;
++ unsigned long sync_cpu_count;
++ unsigned long fail_count;
++ int attr_res;
++#endif
++};
++
++static struct dmabounce_device_info *g_dmabounce_device_info;
++
++extern int bcm2838_dma40_memcpy_init(struct device *dev);
++extern void bcm2838_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size);
++
++#ifdef STATS
++static ssize_t
++bounce_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct dmabounce_device_info *device_info = g_dmabounce_device_info;
++ return sprintf(buf, "m:%lu/%lu s:%lu/%lu f:%lu s:%zu b:%lu/%lu a:%lu/%lu\n",
++ device_info->map_count,
++ device_info->unmap_count,
++ device_info->sync_dev_count,
++ device_info->sync_cpu_count,
++ device_info->fail_count,
++ device_info->pool.max_size,
++ device_info->pool.num_bufs,
++ device_info->pool.max_bufs,
++ device_info->pool.num_pages * PAGE_SIZE,
++ device_info->pool.max_pages * PAGE_SIZE);
++}
++
++static DEVICE_ATTR(dmabounce_stats, 0444, bounce_show, NULL);
++#endif
++
++static int bounce_create(struct dmabounce_pool *pool, struct device *dev,
++ unsigned long buffer_size)
++{
++ int ret = -ENOMEM;
++ pool->pages = (buffer_size + PAGE_SIZE - 1)/PAGE_SIZE;
++ pool->alloc_map = bitmap_zalloc(pool->pages, GFP_KERNEL);
++ if (!pool->alloc_map)
++ goto err_bitmap;
++ pool->virt_addr = dma_alloc_coherent(dev, pool->pages * PAGE_SIZE,
++ &pool->dma_addr, GFP_KERNEL);
++ if (!pool->virt_addr)
++ goto err_dmabuf;
++
++ pool->alloc_pos = 0;
++ spin_lock_init(&pool->lock);
++ pool->dev = dev;
++ pool->num_pages = 0;
++
++ DO_STATS(pool->max_size = 0);
++ DO_STATS(pool->num_bufs = 0);
++ DO_STATS(pool->max_bufs = 0);
++ DO_STATS(pool->max_pages = 0);
++
++ return 0;
++
++err_dmabuf:
++ bitmap_free(pool->alloc_map);
++err_bitmap:
++ return ret;
++}
++
++static void bounce_destroy(struct dmabounce_pool *pool)
++{
++ dma_free_coherent(pool->dev, pool->pages * PAGE_SIZE, pool->virt_addr,
++ pool->dma_addr);
++
++ bitmap_free(pool->alloc_map);
++}
++
++static void *bounce_alloc(struct dmabounce_pool *pool, size_t size,
++ dma_addr_t *dmaaddrp)
++{
++ unsigned long pages;
++ unsigned long flags;
++ unsigned long pos;
++
++ pages = (size + PAGE_SIZE - 1)/PAGE_SIZE;
++
++ DO_STATS(pool->max_size = max(size, pool->max_size));
++
++ spin_lock_irqsave(&pool->lock, flags);
++ pos = bitmap_find_next_zero_area(pool->alloc_map, pool->pages,
++ pool->alloc_pos, pages, 0);
++ /* If not found, try from the start */
++ if (pos >= pool->pages && pool->alloc_pos)
++ pos = bitmap_find_next_zero_area(pool->alloc_map, pool->pages,
++ 0, pages, 0);
++
++ if (pos >= pool->pages) {
++ spin_unlock_irqrestore(&pool->lock, flags);
++ return NULL;
++ }
++
++ bitmap_set(pool->alloc_map, pos, pages);
++ pool->alloc_pos = (pos + pages) % pool->pages;
++ pool->num_pages += pages;
++
++ DO_STATS(pool->num_bufs++);
++ DO_STATS(pool->max_bufs = max(pool->num_bufs, pool->max_bufs));
++ DO_STATS(pool->max_pages = max(pool->num_pages, pool->max_pages));
++
++ spin_unlock_irqrestore(&pool->lock, flags);
++
++ *dmaaddrp = pool->dma_addr + pos * PAGE_SIZE;
++
++ return pool->virt_addr + pos * PAGE_SIZE;
++}
++
++static void
++bounce_free(struct dmabounce_pool *pool, void *buf, size_t size)
++{
++ unsigned long pages;
++ unsigned long flags;
++ unsigned long pos;
++
++ pages = (size + PAGE_SIZE - 1)/PAGE_SIZE;
++ pos = (buf - pool->virt_addr)/PAGE_SIZE;
++
++ BUG_ON((buf - pool->virt_addr) & (PAGE_SIZE - 1));
++
++ spin_lock_irqsave(&pool->lock, flags);
++ bitmap_clear(pool->alloc_map, pos, pages);
++ pool->num_pages -= pages;
++ if (pool->num_pages == 0)
++ pool->alloc_pos = 0;
++ DO_STATS(pool->num_bufs--);
++ spin_unlock_irqrestore(&pool->lock, flags);
++}
++
++/* allocate a 'safe' buffer and keep track of it */
++static struct safe_buffer *
++alloc_safe_buffer(struct dmabounce_device_info *device_info,
++ dma_addr_t dma_addr, size_t size, enum dma_data_direction dir)
++{
++ struct safe_buffer *buf;
++ struct dmabounce_pool *pool = &device_info->pool;
++ struct device *dev = device_info->dev;
++ unsigned long flags;
++
++ /*
++ * Although one might expect this to be called in thread context,
++ * using GFP_KERNEL here leads to hard-to-debug lockups. in_atomic()
++ * was previously used to select the appropriate allocation mode,
++ * but this is unsafe.
++ */
++ buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
++ if (!buf) {
++ dev_warn(dev, "%s: kmalloc failed\n", __func__);
++ return NULL;
++ }
++
++ buf->unsafe_dma_addr = dma_addr;
++ buf->size = size;
++ buf->direction = dir;
++ buf->pool = pool;
++
++ buf->safe = bounce_alloc(pool, size, &buf->safe_dma_addr);
++
++ if (!buf->safe) {
++ dev_warn(dev,
++ "%s: could not alloc dma memory (size=%d)\n",
++ __func__, size);
++ kfree(buf);
++ return NULL;
++ }
++
++ write_lock_irqsave(&device_info->lock, flags);
++ list_add(&buf->node, &device_info->safe_buffers);
++ write_unlock_irqrestore(&device_info->lock, flags);
++
++ return buf;
++}
++
++/* determine if a buffer is from our "safe" pool */
++static struct safe_buffer *
++find_safe_buffer(struct dmabounce_device_info *device_info,
++ dma_addr_t safe_dma_addr)
++{
++ struct safe_buffer *b, *rb = NULL;
++ unsigned long flags;
++
++ read_lock_irqsave(&device_info->lock, flags);
++
++ list_for_each_entry(b, &device_info->safe_buffers, node)
++ if (b->safe_dma_addr <= safe_dma_addr &&
++ b->safe_dma_addr + b->size > safe_dma_addr) {
++ rb = b;
++ break;
++ }
++
++ read_unlock_irqrestore(&device_info->lock, flags);
++ return rb;
++}
++
++static void
++free_safe_buffer(struct dmabounce_device_info *device_info,
++ struct safe_buffer *buf)
++{
++ unsigned long flags;
++
++ write_lock_irqsave(&device_info->lock, flags);
++ list_del(&buf->node);
++ write_unlock_irqrestore(&device_info->lock, flags);
++
++ bounce_free(buf->pool, buf->safe, buf->size);
++
++ kfree(buf);
++}
++
++/* ************************************************** */
++
++static struct safe_buffer *
++find_safe_buffer_dev(struct device *dev, dma_addr_t dma_addr, const char *where)
++{
++ if (!dev || !g_dmabounce_device_info)
++ return NULL;
++ if (dma_mapping_error(dev, dma_addr)) {
++ dev_err(dev, "Trying to %s invalid mapping\n", where);
++ return NULL;
++ }
++ return find_safe_buffer(g_dmabounce_device_info, dma_addr);
++}
++
++static dma_addr_t
++map_single(struct device *dev, struct safe_buffer *buf, size_t size,
++ enum dma_data_direction dir, unsigned long attrs)
++{
++ BUG_ON(buf->size != size);
++ BUG_ON(buf->direction != dir);
++
++ dev_dbg(dev, "map: %llx->%llx\n", (u64)buf->unsafe_dma_addr,
++ (u64)buf->safe_dma_addr);
++
++ if ((dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) &&
++ !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
++ bcm2838_dma40_memcpy(buf->safe_dma_addr, buf->unsafe_dma_addr,
++ size);
++
++ return buf->safe_dma_addr;
++}
++
++static dma_addr_t
++unmap_single(struct device *dev, struct safe_buffer *buf, size_t size,
++ enum dma_data_direction dir, unsigned long attrs)
++{
++ BUG_ON(buf->size != size);
++ BUG_ON(buf->direction != dir);
++
++ if ((dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) &&
++ !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
++ dev_dbg(dev, "unmap: %llx->%llx\n", (u64)buf->safe_dma_addr,
++ (u64)buf->unsafe_dma_addr);
++
++ bcm2838_dma40_memcpy(buf->unsafe_dma_addr, buf->safe_dma_addr,
++ size);
++ }
++ return buf->unsafe_dma_addr;
++}
++
++/* ************************************************** */
++
++/*
++ * see if a buffer address is in an 'unsafe' range. if it is
++ * allocate a 'safe' buffer and copy the unsafe buffer into it.
++ * substitute the safe buffer for the unsafe one.
++ * (basically move the buffer from an unsafe area to a safe one)
++ */
++static dma_addr_t
++dmabounce_map_page(struct device *dev, struct page *page, unsigned long offset,
++ size_t size, enum dma_data_direction dir,
++ unsigned long attrs)
++{
++ struct dmabounce_device_info *device_info = g_dmabounce_device_info;
++ dma_addr_t dma_addr;
++
++ dma_addr = pfn_to_dma(dev, page_to_pfn(page)) + offset;
++
++ arm_dma_ops.sync_single_for_device(dev, dma_addr, size, dir);
++
++ if (device_info && (dma_addr + size) > device_info->threshold) {
++ struct safe_buffer *buf;
++
++ buf = alloc_safe_buffer(device_info, dma_addr, size, dir);
++ if (!buf) {
++ DO_STATS(device_info->fail_count++);
++ return ARM_MAPPING_ERROR;
++ }
++
++ DO_STATS(device_info->map_count++);
++
++ dma_addr = map_single(dev, buf, size, dir, attrs);
++ }
++
++ return dma_addr;
++}
++
++/*
++ * see if a mapped address was really a "safe" buffer and if so, copy
++ * the data from the safe buffer back to the unsafe buffer and free up
++ * the safe buffer. (basically return things back to the way they
++ * should be)
++ */
++static void
++dmabounce_unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
++ enum dma_data_direction dir, unsigned long attrs)
++{
++ struct safe_buffer *buf;
++
++ buf = find_safe_buffer_dev(dev, dma_addr, __func__);
++ if (buf) {
++ DO_STATS(g_dmabounce_device_info->unmap_count++);
++ dma_addr = unmap_single(dev, buf, size, dir, attrs);
++ free_safe_buffer(g_dmabounce_device_info, buf);
++ }
++
++ arm_dma_ops.sync_single_for_cpu(dev, dma_addr, size, dir);
++}
++
++/*
++ * A version of dmabounce_map_page that assumes the mapping has already
++ * been created - intended for streaming operation.
++ */
++static void
++dmabounce_sync_for_device(struct device *dev, dma_addr_t dma_addr, size_t size,
++ enum dma_data_direction dir)
++{
++ struct safe_buffer *buf;
++
++ arm_dma_ops.sync_single_for_device(dev, dma_addr, size, dir);
++
++ buf = find_safe_buffer_dev(dev, dma_addr, __func__);
++ if (buf) {
++ DO_STATS(g_dmabounce_device_info->sync_dev_count++);
++ map_single(dev, buf, size, dir, 0);
++ }
++}
++
++/*
++ * A version of dmabounce_unmap_page that doesn't destroy the mapping -
++ * intended for streaming operation.
++ */
++static void
++dmabounce_sync_for_cpu(struct device *dev, dma_addr_t dma_addr,
++ size_t size, enum dma_data_direction dir)
++{
++ struct safe_buffer *buf;
++
++ buf = find_safe_buffer_dev(dev, dma_addr, __func__);
++ if (buf) {
++ DO_STATS(g_dmabounce_device_info->sync_cpu_count++);
++ dma_addr = unmap_single(dev, buf, size, dir, 0);
++ }
++
++ arm_dma_ops.sync_single_for_cpu(dev, dma_addr, size, dir);
++}
++
++static int dmabounce_dma_supported(struct device *dev, u64 dma_mask)
++{
++ if (g_dmabounce_device_info)
++ return 0;
++
++ return arm_dma_ops.dma_supported(dev, dma_mask);
++}
++
++static int dmabounce_mapping_error(struct device *dev, dma_addr_t dma_addr)
++{
++ return arm_dma_ops.mapping_error(dev, dma_addr);
++}
++
++static const struct dma_map_ops dmabounce_ops = {
++ .alloc = arm_dma_alloc,
++ .free = arm_dma_free,
++ .mmap = arm_dma_mmap,
++ .get_sgtable = arm_dma_get_sgtable,
++ .map_page = dmabounce_map_page,
++ .unmap_page = dmabounce_unmap_page,
++ .sync_single_for_cpu = dmabounce_sync_for_cpu,
++ .sync_single_for_device = dmabounce_sync_for_device,
++ .map_sg = arm_dma_map_sg,
++ .unmap_sg = arm_dma_unmap_sg,
++ .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu,
++ .sync_sg_for_device = arm_dma_sync_sg_for_device,
++ .dma_supported = dmabounce_dma_supported,
++ .mapping_error = dmabounce_mapping_error,
++};
++
++int brcm_pcie_bounce_register_dev(struct device *dev,
++ unsigned long buffer_size,
++ dma_addr_t threshold)
++{
++ struct dmabounce_device_info *device_info;
++ int ret;
++
++ /* Only support a single client */
++ if (g_dmabounce_device_info)
++ return -EBUSY;
++
++ ret = bcm2838_dma40_memcpy_init(dev);
++ if (ret)
++ return ret;
++
++ device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
++ if (!device_info) {
++ dev_err(dev,
++ "Could not allocated dmabounce_device_info\n");
++ return -ENOMEM;
++ }
++
++ ret = bounce_create(&device_info->pool, dev, buffer_size);
++ if (ret) {
++ dev_err(dev,
++ "dmabounce: could not allocate %ld byte DMA pool\n",
++ buffer_size);
++ goto err_bounce;
++ }
++
++ device_info->dev = dev;
++ device_info->threshold = threshold;
++ INIT_LIST_HEAD(&device_info->safe_buffers);
++ rwlock_init(&device_info->lock);
++
++ DO_STATS(device_info->map_count = 0);
++ DO_STATS(device_info->unmap_count = 0);
++ DO_STATS(device_info->sync_dev_count = 0);
++ DO_STATS(device_info->sync_cpu_count = 0);
++ DO_STATS(device_info->fail_count = 0);
++ DO_STATS(device_info->attr_res =
++ device_create_file(dev, &dev_attr_dmabounce_stats));
++
++ g_dmabounce_device_info = device_info;
++ set_dma_ops(dev, &dmabounce_ops);
++
++ dev_info(dev, "dmabounce: registered device - %ld kB, threshold %pad\n",
++ buffer_size / 1024, &threshold);
++
++ return 0;
++
++ err_bounce:
++ kfree(device_info);
++ return ret;
++}
++EXPORT_SYMBOL(brcm_pcie_bounce_register_dev);
++
++void brcm_pcie_bounce_unregister_dev(struct device *dev)
++{
++ struct dmabounce_device_info *device_info = g_dmabounce_device_info;
++
++ g_dmabounce_device_info = NULL;
++ set_dma_ops(dev, NULL);
++
++ if (!device_info) {
++ dev_warn(dev,
++ "Never registered with dmabounce but attempting"
++ "to unregister!\n");
++ return;
++ }
++
++ if (!list_empty(&device_info->safe_buffers)) {
++ dev_err(dev,
++ "Removing from dmabounce with pending buffers!\n");
++ BUG();
++ }
++
++ bounce_destroy(&device_info->pool);
++
++ DO_STATS(if (device_info->attr_res == 0)
++ device_remove_file(dev, &dev_attr_dmabounce_stats));
++
++ kfree(device_info);
++
++ dev_info(dev, "dmabounce: device unregistered\n");
++}
++EXPORT_SYMBOL(brcm_pcie_bounce_unregister_dev);
++
++MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.org>");
++MODULE_DESCRIPTION("Dedicate DMA bounce support for pcie-brcmstb");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/pci/controller/pcie-brcmstb-bounce.h
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2019 Raspberry Pi (Trading) Ltd.
++ */
++
++#ifndef _PCIE_BRCMSTB_BOUNCE_H
++#define _PCIE_BRCMSTB_BOUNCE_H
++
++#ifdef CONFIG_ARM
++
++int brcm_pcie_bounce_register_dev(struct device *dev, unsigned long buffer_size,
++ dma_addr_t threshold);
++
++int brcm_pcie_bounce_unregister_dev(struct device *dev);
++
++#else
++
++static inline int brcm_pcie_bounce_register_dev(struct device *dev,
++ unsigned long buffer_size,
++ dma_addr_t threshold)
++{
++ return 0;
++}
++
++static inline int brcm_pcie_bounce_unregister_dev(struct device *dev)
++{
++ return 0;
++}
++
++#endif
++
++#endif /* _PCIE_BRCMSTB_BOUNCE_H */
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -29,6 +29,7 @@
+ #include <linux/string.h>
+ #include <linux/types.h>
+ #include "../pci.h"
++#include "pcie-brcmstb-bounce.h"
+
+ /* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
+ #define BRCM_PCIE_CAP_REGS 0x00ac
+@@ -53,6 +54,7 @@
+ #define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
+ #define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
+ #define PCIE_MISC_MSI_DATA_CONFIG 0x404c
++#define PCIE_MISC_EOI_CTRL 0x4060
+ #define PCIE_MISC_PCIE_CTRL 0x4064
+ #define PCIE_MISC_PCIE_STATUS 0x4068
+ #define PCIE_MISC_REVISION 0x406c
+@@ -260,12 +262,14 @@ struct brcm_pcie {
+ unsigned int rev;
+ const int *reg_offsets;
+ const int *reg_field_info;
++ u32 max_burst_size;
+ enum pcie_type type;
+ };
+
+ struct pcie_cfg_data {
+ const int *reg_field_info;
+ const int *offsets;
++ const u32 max_burst_size;
+ const enum pcie_type type;
+ };
+
+@@ -288,24 +292,27 @@ static const int pcie_offset_bcm7425[] =
+ static const struct pcie_cfg_data bcm7425_cfg = {
+ .reg_field_info = pcie_reg_field_info,
+ .offsets = pcie_offset_bcm7425,
++ .max_burst_size = BURST_SIZE_256,
+ .type = BCM7425,
+ };
+
+ static const int pcie_offsets[] = {
+ [RGR1_SW_INIT_1] = 0x9210,
+ [EXT_CFG_INDEX] = 0x9000,
+- [EXT_CFG_DATA] = 0x9004,
++ [EXT_CFG_DATA] = 0x8000,
+ };
+
+ static const struct pcie_cfg_data bcm7435_cfg = {
+ .reg_field_info = pcie_reg_field_info,
+ .offsets = pcie_offsets,
++ .max_burst_size = BURST_SIZE_256,
+ .type = BCM7435,
+ };
+
+ static const struct pcie_cfg_data generic_cfg = {
+ .reg_field_info = pcie_reg_field_info,
+ .offsets = pcie_offsets,
++ .max_burst_size = BURST_SIZE_128, // before BURST_SIZE_512
+ .type = GENERIC,
+ };
+
+@@ -318,6 +325,7 @@ static const int pcie_offset_bcm7278[] =
+ static const struct pcie_cfg_data bcm7278_cfg = {
+ .reg_field_info = pcie_reg_field_info_bcm7278,
+ .offsets = pcie_offset_bcm7278,
++ .max_burst_size = BURST_SIZE_512,
+ .type = BCM7278,
+ };
+
+@@ -360,7 +368,6 @@ static struct pci_ops brcm_pcie_ops = {
+ (reg##_##field##_MASK & (field_val << reg##_##field##_SHIFT)))
+
+ static const struct dma_map_ops *arch_dma_ops;
+-static const struct dma_map_ops *brcm_dma_ops_ptr;
+ static struct of_pci_range *dma_ranges;
+ static int num_dma_ranges;
+
+@@ -369,6 +376,16 @@ static int num_memc;
+ static int num_pcie;
+ static DEFINE_MUTEX(brcm_pcie_lock);
+
++static unsigned int bounce_buffer = 32*1024*1024;
++module_param(bounce_buffer, uint, 0644);
++MODULE_PARM_DESC(bounce_buffer, "Size of bounce buffer");
++
++static unsigned int bounce_threshold = 0xc0000000;
++module_param(bounce_threshold, uint, 0644);
++MODULE_PARM_DESC(bounce_threshold, "Bounce threshold");
++
++static struct brcm_pcie *g_pcie;
++
+ static dma_addr_t brcm_to_pci(dma_addr_t addr)
+ {
+ struct of_pci_range *p;
+@@ -457,12 +474,10 @@ static int brcm_map_sg(struct device *de
+ struct scatterlist *sg;
+
+ for_each_sg(sgl, sg, nents, i) {
+-#ifdef CONFIG_NEED_SG_DMA_LENGTH
+- sg->dma_length = sg->length;
+-#endif
++ sg_dma_len(sg) = sg->length;
+ sg->dma_address =
+- brcm_dma_ops_ptr->map_page(dev, sg_page(sg), sg->offset,
+- sg->length, dir, attrs);
++ brcm_map_page(dev, sg_page(sg), sg->offset,
++ sg->length, dir, attrs);
+ if (dma_mapping_error(dev, sg->dma_address))
+ goto bad_mapping;
+ }
+@@ -470,8 +485,8 @@ static int brcm_map_sg(struct device *de
+
+ 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);
++ brcm_unmap_page(dev, sg_dma_address(sg),
++ sg_dma_len(sg), dir, attrs);
+ return 0;
+ }
+
+@@ -484,8 +499,8 @@ static void brcm_unmap_sg(struct device
+ 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);
++ brcm_unmap_page(dev, sg_dma_address(sg),
++ sg_dma_len(sg), dir, attrs);
+ }
+
+ static void brcm_sync_single_for_cpu(struct device *dev,
+@@ -531,8 +546,8 @@ void brcm_sync_sg_for_cpu(struct device
+ 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);
++ brcm_sync_single_for_cpu(dev, sg_dma_address(sg),
++ sg->length, dir);
+ }
+
+ void brcm_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
+@@ -542,9 +557,9 @@ void brcm_sync_sg_for_device(struct devi
+ 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);
++ brcm_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)
+@@ -633,17 +648,47 @@ static void brcm_set_dma_ops(struct devi
+ set_dma_ops(dev, &brcm_dma_ops);
+ }
+
++static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie,
++ unsigned int val);
+ static int brcmstb_platform_notifier(struct notifier_block *nb,
+ unsigned long event, void *__dev)
+ {
++ extern unsigned long max_pfn;
+ struct device *dev = __dev;
++ const char *rc_name = "0000:00:00.0";
+
+- brcm_dma_ops_ptr = &brcm_dma_ops;
+- if (event != BUS_NOTIFY_ADD_DEVICE)
+- return NOTIFY_DONE;
++ switch (event) {
++ case BUS_NOTIFY_ADD_DEVICE:
++ if (max_pfn > (bounce_threshold/PAGE_SIZE) &&
++ strcmp(dev->kobj.name, rc_name)) {
++ int ret;
++
++ ret = brcm_pcie_bounce_register_dev(dev, bounce_buffer,
++ (dma_addr_t)bounce_threshold);
++ if (ret) {
++ dev_err(dev,
++ "brcm_pcie_bounce_register_dev() failed: %d\n",
++ ret);
++ return ret;
++ }
++ }
++ brcm_set_dma_ops(dev);
++ return NOTIFY_OK;
++
++ case BUS_NOTIFY_DEL_DEVICE:
++ if (!strcmp(dev->kobj.name, rc_name) && g_pcie) {
++ /* Force a bus reset */
++ brcm_pcie_perst_set(g_pcie, 1);
++ msleep(100);
++ brcm_pcie_perst_set(g_pcie, 0);
++ } else if (max_pfn > (bounce_threshold/PAGE_SIZE)) {
++ brcm_pcie_bounce_unregister_dev(dev);
++ }
++ return NOTIFY_OK;
+
+- brcm_set_dma_ops(dev);
+- return NOTIFY_OK;
++ default:
++ return NOTIFY_DONE;
++ }
+ }
+
+ static struct notifier_block brcmstb_platform_nb = {
+@@ -914,6 +959,7 @@ static void brcm_pcie_msi_isr(struct irq
+ }
+ }
+ chained_irq_exit(chip, desc);
++ bcm_writel(1, msi->base + PCIE_MISC_EOI_CTRL);
+ }
+
+ static void brcm_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+@@ -930,7 +976,8 @@ static void brcm_compose_msi_msg(struct
+ static int brcm_msi_set_affinity(struct irq_data *irq_data,
+ const struct cpumask *mask, bool force)
+ {
+- return -EINVAL;
++ struct brcm_msi *msi = irq_data_get_irq_chip_data(irq_data);
++ return __irq_set_affinity(msi->irq, mask, force);
+ }
+
+ static struct irq_chip brcm_msi_bottom_irq_chip = {
+@@ -1168,9 +1215,9 @@ static void __iomem *brcm_pcie_map_conf(
+ return PCI_SLOT(devfn) ? NULL : base + where;
+
+ /* For devices, write to the config space index register */
+- idx = cfg_index(bus->number, devfn, where);
++ idx = cfg_index(bus->number, devfn, 0);
+ bcm_writel(idx, pcie->base + IDX_ADDR(pcie));
+- return base + DATA_ADDR(pcie) + (where & 0x3);
++ return base + DATA_ADDR(pcie) + where;
+ }
+
+ static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie,
+@@ -1238,20 +1285,6 @@ static int brcm_pcie_parse_map_dma_range
+ 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);
+- return -EINVAL;
+- } else if (size) {
+- scb_size[i] = roundup_pow_of_two_64(size);
+- num_memc++;
+- } else {
+- break;
+- }
+- }
+-
+ return 0;
+ }
+
+@@ -1275,26 +1308,25 @@ static int brcm_pcie_add_controller(stru
+ 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);
+-
+- if (size == (u64)-1) {
+- dev_err(dev, "cannot get memc%d size\n", i);
+- ret = -EINVAL;
+- goto done;
+- } else if (size) {
+- scb_size[i] = roundup_pow_of_two_64(size);
+- num_memc++;
+- } else {
+- break;
++ if (!num_dma_ranges) {
++ /* Determine num_memc and their sizes by other means */
++ for (i = 0, num_memc = 0; i < BRCM_MAX_SCB; i++) {
++ u64 size = brcmstb_memory_memc_size(i);
++
++ if (size == (u64)-1) {
++ dev_err(dev, "cannot get memc%d size\n", i);
++ ret = -EINVAL;
++ goto done;
++ } else if (size) {
++ scb_size[i] = roundup_pow_of_two_64(size);
++ } else {
++ break;
++ }
+ }
+- }
+- if (!ret && num_memc == 0) {
+- ret = -EINVAL;
+- goto done;
++ num_memc = i;
+ }
+
++ g_pcie = pcie;
+ num_pcie++;
+ done:
+ mutex_unlock(&brcm_pcie_lock);
+@@ -1307,6 +1339,7 @@ static void brcm_pcie_remove_controller(
+ if (--num_pcie > 0)
+ goto out;
+
++ g_pcie = NULL;
+ if (brcm_unregister_notifier())
+ dev_err(pcie->dev, "failed to unregister pci bus notifier\n");
+ kfree(dma_ranges);
+@@ -1367,7 +1400,7 @@ static int brcm_pcie_setup(struct brcm_p
+ void __iomem *base = pcie->base;
+ unsigned int scb_size_val;
+ u64 rc_bar2_offset, rc_bar2_size, total_mem_size = 0;
+- u32 tmp, burst;
++ u32 tmp;
+ int i, j, ret, limit;
+ u16 nlw, cls, lnksta;
+ bool ssc_good = false;
+@@ -1400,20 +1433,15 @@ static int brcm_pcie_setup(struct brcm_p
+ /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
+ tmp = INSERT_FIELD(0, PCIE_MISC_MISC_CTRL, SCB_ACCESS_EN, 1);
+ tmp = INSERT_FIELD(tmp, PCIE_MISC_MISC_CTRL, CFG_READ_UR_MODE, 1);
+- burst = (pcie->type == GENERIC || pcie->type == BCM7278)
+- ? BURST_SIZE_512 : BURST_SIZE_256;
+- tmp = INSERT_FIELD(tmp, PCIE_MISC_MISC_CTRL, MAX_BURST_SIZE, burst);
++ tmp = INSERT_FIELD(tmp, PCIE_MISC_MISC_CTRL, MAX_BURST_SIZE,
++ pcie->max_burst_size);
+ bcm_writel(tmp, base + PCIE_MISC_MISC_CTRL);
+
+ /*
+ * Set up inbound memory view for the EP (called RC_BAR2,
+ * not to be confused with the BARs that are advertised by
+ * the EP).
+- */
+- for (i = 0; i < num_memc; i++)
+- total_mem_size += scb_size[i];
+-
+- /*
++ *
+ * The PCIe host controller by design must set the inbound
+ * viewport to be a contiguous arrangement of all of the
+ * system's memory. In addition, its size mut be a power of
+@@ -1429,55 +1457,49 @@ static int brcm_pcie_setup(struct brcm_p
+ * the controller will know to send outbound memory downstream
+ * and everything else upstream.
+ */
+- rc_bar2_size = roundup_pow_of_two_64(total_mem_size);
+
+- if (dma_ranges) {
++ if (num_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.
++ * Use the base address and size(s) provided in the dma-ranges
++ * property.
+ */
+- if (total_mem_size <= 0xc0000000ULL &&
+- rc_bar2_size <= 0x100000000ULL) {
+- rc_bar2_offset = 0;
+- /* If the viewport is less then 4GB we can fit
+- * the MSI target address under 4GB. Otherwise
+- * put it right below 64GB.
+- */
+- msi_target_addr =
+- (rc_bar2_size == 0x100000000ULL)
+- ? BRCM_MSI_TARGET_ADDR_GT_4GB
+- : BRCM_MSI_TARGET_ADDR_LT_4GB;
+- } 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;
+-
+- /* Since we are starting the viewport at 4GB or
+- * higher, put the MSI target address below 4GB
+- */
+- msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
+- }
+- } else {
++ for (i = 0; i < num_dma_ranges; i++)
++ scb_size[i] = roundup_pow_of_two_64(dma_ranges[i].size);
++
++ num_memc = num_dma_ranges;
++ rc_bar2_offset = dma_ranges[0].pci_addr;
++ } else if (num_memc) {
+ /*
+ * Set simple configuration based on memory sizes
+- * only. We always start the viewport at address 0,
+- * and set the MSI target address accordingly.
++ * only. We always start the viewport at address 0.
+ */
+ rc_bar2_offset = 0;
++ } else {
++ return -EINVAL;
++ }
++
++ for (i = 0; i < num_memc; i++)
++ total_mem_size += scb_size[i];
++
++ rc_bar2_size = roundup_pow_of_two_64(total_mem_size);
+
+- msi_target_addr = (rc_bar2_size >= 0x100000000ULL)
+- ? BRCM_MSI_TARGET_ADDR_GT_4GB
+- : BRCM_MSI_TARGET_ADDR_LT_4GB;
++ /* Verify the alignment is correct */
++ if (rc_bar2_offset & (rc_bar2_size - 1)) {
++ dev_err(dev, "inbound window is misaligned\n");
++ return -EINVAL;
+ }
++
++ /*
++ * Position the MSI target low if possible.
++ *
++ * TO DO: Consider outbound window when choosing MSI target and
++ * verifying configuration.
++ */
++ msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
++ if (rc_bar2_offset <= msi_target_addr &&
++ rc_bar2_offset + rc_bar2_size > msi_target_addr)
++ msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
++
+ pcie->msi_target_addr = msi_target_addr;
+
+ tmp = lower_32_bits(rc_bar2_offset);
+@@ -1713,6 +1735,7 @@ static int brcm_pcie_probe(struct platfo
+ data = of_id->data;
+ pcie->reg_offsets = data->offsets;
+ pcie->reg_field_info = data->reg_field_info;
++ pcie->max_burst_size = data->max_burst_size;
+ pcie->type = data->type;
+ pcie->dn = dn;
+ pcie->dev = &pdev->dev;
+@@ -1732,7 +1755,7 @@ static int brcm_pcie_probe(struct platfo
+
+ pcie->clk = of_clk_get_by_name(dn, "sw_pcie");
+ if (IS_ERR(pcie->clk)) {
+- dev_err(&pdev->dev, "could not get clock\n");
++ dev_warn(&pdev->dev, "could not get clock\n");
+ pcie->clk = NULL;
+ }
+ pcie->base = base;
+@@ -1755,7 +1778,8 @@ static int brcm_pcie_probe(struct platfo
+
+ ret = clk_prepare_enable(pcie->clk);
+ if (ret) {
+- dev_err(&pdev->dev, "could not enable clock\n");
++ if (ret != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "could not enable clock\n");
+ return ret;
+ }
+
+@@ -1818,7 +1842,6 @@ static struct platform_driver brcm_pcie_
+ .remove = brcm_pcie_remove,
+ .driver = {
+ .name = "brcm-pcie",
+- .owner = THIS_MODULE,
+ .of_match_table = brcm_pcie_match,
+ .pm = &brcm_pcie_pm_ops,
+ },
+--- a/drivers/soc/bcm/brcmstb/Makefile
++++ b/drivers/soc/bcm/brcmstb/Makefile
+@@ -1,2 +1,2 @@
+-obj-y += common.o biuctrl.o
++obj-y += common.o biuctrl.o memory.o
+ obj-$(CONFIG_BRCMSTB_PM) += pm/
+--- /dev/null
++++ b/drivers/soc/bcm/brcmstb/memory.c
+@@ -0,0 +1,158 @@
++// SPDX-License-Identifier: GPL-2.0
++/* Copyright © 2015-2017 Broadcom */
++
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/libfdt.h>
++#include <linux/of_address.h>
++#include <linux/of_fdt.h>
++#include <linux/sizes.h>
++#include <soc/brcmstb/memory_api.h>
++
++/* Macro to help extract property data */
++#define DT_PROP_DATA_TO_U32(b, offs) (fdt32_to_cpu(*(u32 *)(b + offs)))
++
++/* Constants used when retrieving memc info */
++#define NUM_BUS_RANGES 10
++#define BUS_RANGE_ULIMIT_SHIFT 4
++#define BUS_RANGE_LLIMIT_SHIFT 4
++#define BUS_RANGE_PA_SHIFT 12
++
++enum {
++ BUSNUM_MCP0 = 0x4,
++ BUSNUM_MCP1 = 0x5,
++ BUSNUM_MCP2 = 0x6,
++};
++
++/*
++ * If the DT nodes are handy, determine which MEMC holds the specified
++ * physical address.
++ */
++#ifdef CONFIG_ARCH_BRCMSTB
++int __brcmstb_memory_phys_addr_to_memc(phys_addr_t pa, void __iomem *base)
++{
++ int memc = -1;
++ int i;
++
++ for (i = 0; i < NUM_BUS_RANGES; i++, base += 8) {
++ const u64 ulimit_raw = readl(base);
++ const u64 llimit_raw = readl(base + 4);
++ const u64 ulimit =
++ ((ulimit_raw >> BUS_RANGE_ULIMIT_SHIFT)
++ << BUS_RANGE_PA_SHIFT) | 0xfff;
++ const u64 llimit = (llimit_raw >> BUS_RANGE_LLIMIT_SHIFT)
++ << BUS_RANGE_PA_SHIFT;
++ const u32 busnum = (u32)(ulimit_raw & 0xf);
++
++ if (pa >= llimit && pa <= ulimit) {
++ if (busnum >= BUSNUM_MCP0 && busnum <= BUSNUM_MCP2) {
++ memc = busnum - BUSNUM_MCP0;
++ break;
++ }
++ }
++ }
++
++ return memc;
++}
++
++int brcmstb_memory_phys_addr_to_memc(phys_addr_t pa)
++{
++ int memc = -1;
++ struct device_node *np;
++ void __iomem *cpubiuctrl;
++
++ np = of_find_compatible_node(NULL, NULL, "brcm,brcmstb-cpu-biu-ctrl");
++ if (!np)
++ return memc;
++
++ cpubiuctrl = of_iomap(np, 0);
++ if (!cpubiuctrl)
++ goto cleanup;
++
++ memc = __brcmstb_memory_phys_addr_to_memc(pa, cpubiuctrl);
++ iounmap(cpubiuctrl);
++
++cleanup:
++ of_node_put(np);
++
++ return memc;
++}
++
++#elif defined(CONFIG_MIPS)
++int brcmstb_memory_phys_addr_to_memc(phys_addr_t pa)
++{
++ /* The logic here is fairly simple and hardcoded: if pa <= 0x5000_0000,
++ * then this is MEMC0, else MEMC1.
++ *
++ * For systems with 2GB on MEMC0, MEMC1 starts at 9000_0000, with 1GB
++ * on MEMC0, MEMC1 starts at 6000_0000.
++ */
++ if (pa >= 0x50000000ULL)
++ return 1;
++ else
++ return 0;
++}
++#endif
++
++u64 brcmstb_memory_memc_size(int memc)
++{
++ const void *fdt = initial_boot_params;
++ const int mem_offset = fdt_path_offset(fdt, "/memory");
++ int addr_cells = 1, size_cells = 1;
++ const struct fdt_property *prop;
++ int proplen, cellslen;
++ u64 memc_size = 0;
++ int i;
++
++ /* Get root size and address cells if specified */
++ prop = fdt_get_property(fdt, 0, "#size-cells", &proplen);
++ if (prop)
++ size_cells = DT_PROP_DATA_TO_U32(prop->data, 0);
++
++ prop = fdt_get_property(fdt, 0, "#address-cells", &proplen);
++ if (prop)
++ addr_cells = DT_PROP_DATA_TO_U32(prop->data, 0);
++
++ if (mem_offset < 0)
++ return -1;
++
++ prop = fdt_get_property(fdt, mem_offset, "reg", &proplen);
++ cellslen = (int)sizeof(u32) * (addr_cells + size_cells);
++ if ((proplen % cellslen) != 0)
++ return -1;
++
++ for (i = 0; i < proplen / cellslen; ++i) {
++ u64 addr = 0;
++ u64 size = 0;
++ int memc_idx;
++ int j;
++
++ for (j = 0; j < addr_cells; ++j) {
++ int offset = (cellslen * i) + (sizeof(u32) * j);
++
++ addr |= (u64)DT_PROP_DATA_TO_U32(prop->data, offset) <<
++ ((addr_cells - j - 1) * 32);
++ }
++ for (j = 0; j < size_cells; ++j) {
++ int offset = (cellslen * i) +
++ (sizeof(u32) * (j + addr_cells));
++
++ size |= (u64)DT_PROP_DATA_TO_U32(prop->data, offset) <<
++ ((size_cells - j - 1) * 32);
++ }
++
++ if ((phys_addr_t)addr != addr) {
++ pr_err("phys_addr_t is smaller than provided address 0x%llx!\n",
++ addr);
++ return -1;
++ }
++
++ memc_idx = brcmstb_memory_phys_addr_to_memc((phys_addr_t)addr);
++ if (memc_idx == memc)
++ memc_size += size;
++ }
++
++ return memc_size;
++}
++EXPORT_SYMBOL_GPL(brcmstb_memory_memc_size);
++