Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is subject to the terms and conditions of the GNU General Public |
| 3 | * License. See the file "COPYING" in the main directory of this archive |
| 4 | * for more details. |
| 5 | * |
| 6 | * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com> |
| 7 | */ |
| 8 | |
| 9 | #define pr_fmt(fmt) "bmips-dma: " fmt |
| 10 | |
| 11 | #include <linux/device.h> |
| 12 | #include <linux/dma-direction.h> |
| 13 | #include <linux/dma-mapping.h> |
| 14 | #include <linux/init.h> |
| 15 | #include <linux/io.h> |
| 16 | #include <linux/of.h> |
| 17 | #include <linux/printk.h> |
| 18 | #include <linux/slab.h> |
| 19 | #include <linux/types.h> |
| 20 | #include <asm/bmips.h> |
| 21 | |
| 22 | /* |
| 23 | * BCM338x has configurable address translation windows which allow the |
| 24 | * peripherals' DMA addresses to be different from the Zephyr-visible |
| 25 | * physical addresses. e.g. usb_dma_addr = zephyr_pa ^ 0x08000000 |
| 26 | * |
| 27 | * If the "brcm,ubus" node has a "dma-ranges" property we will enable this |
| 28 | * translation globally using the provided information. This implements a |
| 29 | * very limited subset of "dma-ranges" support and it will probably be |
| 30 | * replaced by a more generic version later. |
| 31 | */ |
| 32 | |
| 33 | struct bmips_dma_range { |
| 34 | u32 child_addr; |
| 35 | u32 parent_addr; |
| 36 | u32 size; |
| 37 | }; |
| 38 | |
| 39 | static struct bmips_dma_range *bmips_dma_ranges; |
| 40 | |
| 41 | #define FLUSH_RAC 0x100 |
| 42 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 43 | dma_addr_t phys_to_dma(struct device *dev, phys_addr_t pa) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 44 | { |
| 45 | struct bmips_dma_range *r; |
| 46 | |
| 47 | for (r = bmips_dma_ranges; r && r->size; r++) { |
| 48 | if (pa >= r->child_addr && |
| 49 | pa < (r->child_addr + r->size)) |
| 50 | return pa - r->child_addr + r->parent_addr; |
| 51 | } |
| 52 | return pa; |
| 53 | } |
| 54 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 55 | phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 56 | { |
| 57 | struct bmips_dma_range *r; |
| 58 | |
| 59 | for (r = bmips_dma_ranges; r && r->size; r++) { |
| 60 | if (dma_addr >= r->parent_addr && |
| 61 | dma_addr < (r->parent_addr + r->size)) |
| 62 | return dma_addr - r->parent_addr + r->child_addr; |
| 63 | } |
| 64 | return dma_addr; |
| 65 | } |
| 66 | |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 67 | void arch_sync_dma_for_cpu_all(void) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 68 | { |
| 69 | void __iomem *cbr = BMIPS_GET_CBR(); |
| 70 | u32 cfg; |
| 71 | |
| 72 | if (boot_cpu_type() != CPU_BMIPS3300 && |
| 73 | boot_cpu_type() != CPU_BMIPS4350 && |
| 74 | boot_cpu_type() != CPU_BMIPS4380) |
| 75 | return; |
| 76 | |
| 77 | /* Flush stale data out of the readahead cache */ |
| 78 | cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG); |
| 79 | __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG); |
| 80 | __raw_readl(cbr + BMIPS_RAC_CONFIG); |
| 81 | } |
| 82 | |
| 83 | static int __init bmips_init_dma_ranges(void) |
| 84 | { |
| 85 | struct device_node *np = |
| 86 | of_find_compatible_node(NULL, NULL, "brcm,ubus"); |
| 87 | const __be32 *data; |
| 88 | struct bmips_dma_range *r; |
| 89 | int len; |
| 90 | |
| 91 | if (!np) |
| 92 | return 0; |
| 93 | |
| 94 | data = of_get_property(np, "dma-ranges", &len); |
| 95 | if (!data) |
| 96 | goto out_good; |
| 97 | |
| 98 | len /= sizeof(*data) * 3; |
| 99 | if (!len) |
| 100 | goto out_bad; |
| 101 | |
| 102 | /* add a dummy (zero) entry at the end as a sentinel */ |
| 103 | bmips_dma_ranges = kcalloc(len + 1, sizeof(struct bmips_dma_range), |
| 104 | GFP_KERNEL); |
| 105 | if (!bmips_dma_ranges) |
| 106 | goto out_bad; |
| 107 | |
| 108 | for (r = bmips_dma_ranges; len; len--, r++) { |
| 109 | r->child_addr = be32_to_cpup(data++); |
| 110 | r->parent_addr = be32_to_cpup(data++); |
| 111 | r->size = be32_to_cpup(data++); |
| 112 | } |
| 113 | |
| 114 | out_good: |
| 115 | of_node_put(np); |
| 116 | return 0; |
| 117 | |
| 118 | out_bad: |
| 119 | pr_err("error parsing dma-ranges property\n"); |
| 120 | of_node_put(np); |
| 121 | return -EINVAL; |
| 122 | } |
| 123 | arch_initcall(bmips_init_dma_ranges); |