diff options
author | Álvaro Fernández Rojas <noltari@gmail.com> | 2020-03-16 20:41:03 +0100 |
---|---|---|
committer | Álvaro Fernández Rojas <noltari@gmail.com> | 2020-03-16 21:24:23 +0100 |
commit | 0f6d04457a64f2393b05f662dc8d381ea1963c66 (patch) | |
tree | 2d6a3c82d5e6d02b030b16df66da8bf9209b02ec /target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch | |
parent | d890f85e59617228b1e76e8cf45a9309904d9124 (diff) | |
download | upstream-0f6d04457a64f2393b05f662dc8d381ea1963c66.tar.gz upstream-0f6d04457a64f2393b05f662dc8d381ea1963c66.tar.bz2 upstream-0f6d04457a64f2393b05f662dc8d381ea1963c66.zip |
bcm27xx: sync 5.4 patches with RPi Foundation
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch | 569 |
1 files changed, 0 insertions, 569 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch b/target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch deleted file mode 100644 index 329b6e268c..0000000000 --- a/target/linux/bcm27xx/patches-5.4/950-0206-PCI-brcmstb-Add-dma-range-mapping-for-inbound-traffi.patch +++ /dev/null @@ -1,569 +0,0 @@ -From d45590eb858ac7a2578d477791881ba7ffb1e615 Mon Sep 17 00:00:00 2001 -From: Phil Elwell <phil@raspberrypi.org> -Date: Tue, 19 Feb 2019 22:06:59 +0000 -Subject: [PATCH] PCI: brcmstb: Add dma-range mapping for inbound - traffic - -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@gmail.com> ---- - drivers/pci/controller/pcie-brcmstb.c | 420 +++++++++++++++++++++++++- - 1 file changed, 411 insertions(+), 9 deletions(-) - ---- a/drivers/pci/controller/pcie-brcmstb.c -+++ b/drivers/pci/controller/pcie-brcmstb.c -@@ -4,6 +4,7 @@ - #include <linux/clk.h> - #include <linux/compiler.h> - #include <linux/delay.h> -+#include <linux/dma-mapping.h> - #include <linux/init.h> - #include <linux/interrupt.h> - #include <linux/io.h> -@@ -319,11 +320,307 @@ static struct pci_ops brcm_pcie_ops = { - ((val & ~reg##_##field##_MASK) | \ - (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; -+ - static phys_addr_t scb_size[BRCM_MAX_SCB]; - static int num_memc; - static int num_pcie; - static DEFINE_MUTEX(brcm_pcie_lock); - -+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, true); -+ 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, -+}; -+ -+static int brcm_register_notifier(void) -+{ -+ return bus_register_notifier(&pci_bus_type, &brcmstb_platform_nb); -+} -+ -+static int brcm_unregister_notifier(void) -+{ -+ return bus_unregister_notifier(&pci_bus_type, &brcmstb_platform_nb); -+} -+ - static u32 rd_fld(void __iomem *p, u32 mask, int shift) - { - return (bcm_readl(p) & mask) >> shift; -@@ -597,9 +894,71 @@ static inline void brcm_pcie_perst_set(s - WR_FLD_RB(pcie->base, PCIE_MISC_PCIE_CTRL, PCIE_PERSTB, !val); - } - -+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; -+ 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 (!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) -+ return -ENOMEM; -+ -+ 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); -+ return -EINVAL; -+ } else if (size) { -+ scb_size[i] = roundup_pow_of_two_64(size); -+ num_memc++; -+ } else { -+ break; -+ } -+ } -+ -+ return 0; -+} -+ - static int brcm_pcie_add_controller(struct brcm_pcie *pcie) - { - int i, ret = 0; -+ struct device *dev = pcie->dev; - - mutex_lock(&brcm_pcie_lock); - if (num_pcie > 0) { -@@ -607,12 +966,21 @@ static int brcm_pcie_add_controller(stru - goto done; - } - -+ ret = brcm_register_notifier(); -+ if (ret) { -+ dev_err(dev, "failed to register pci bus notifier\n"); -+ 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); - - if (size == (u64)-1) { -- dev_err(pcie->dev, "cannot get memc%d size\n", i); -+ dev_err(dev, "cannot get memc%d size\n", i); - ret = -EINVAL; - goto done; - } else if (size) { -@@ -636,8 +1004,16 @@ done: - 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; -+ -+ if (brcm_unregister_notifier()) -+ dev_err(pcie->dev, "failed to unregister pci bus notifier\n"); -+ kfree(dma_ranges); -+ dma_ranges = NULL; -+ num_dma_ranges = 0; -+ num_memc = 0; -+out: - mutex_unlock(&brcm_pcie_lock); - } - -@@ -757,6 +1133,38 @@ static int brcm_pcie_setup(struct brcm_p - */ - 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)); -@@ -967,7 +1375,6 @@ static int brcm_pcie_probe(struct platfo - struct brcm_pcie *pcie; - struct resource *res; - void __iomem *base; -- u32 tmp; - struct pci_host_bridge *bridge; - struct pci_bus *child; - -@@ -984,11 +1391,6 @@ static int brcm_pcie_probe(struct platfo - 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; |