Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [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 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 9 | #define pr_fmt(fmt) "bmips-dma: " fmt |
| 10 | |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 11 | #include <linux/device.h> |
| 12 | #include <linux/dma-direction.h> |
| 13 | #include <linux/dma-mapping.h> |
| 14 | #include <linux/init.h> |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 15 | #include <linux/io.h> |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 16 | #include <linux/of.h> |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 17 | #include <linux/printk.h> |
| 18 | #include <linux/slab.h> |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 19 | #include <linux/types.h> |
| 20 | #include <dma-coherence.h> |
| 21 | |
| 22 | /* |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 23 | * BCM338x has configurable address translation windows which allow the |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 24 | * peripherals' DMA addresses to be different from the Zephyr-visible |
| 25 | * physical addresses. e.g. usb_dma_addr = zephyr_pa ^ 0x08000000 |
| 26 | * |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 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. |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 31 | */ |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 32 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 33 | struct bmips_dma_range { |
| 34 | u32 child_addr; |
| 35 | u32 parent_addr; |
| 36 | u32 size; |
| 37 | }; |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 38 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 39 | static struct bmips_dma_range *bmips_dma_ranges; |
| 40 | |
| 41 | #define FLUSH_RAC 0x100 |
| 42 | |
| 43 | static dma_addr_t bmips_phys_to_dma(struct device *dev, phys_addr_t pa) |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 44 | { |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 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 | } |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 52 | return pa; |
| 53 | } |
| 54 | |
| 55 | dma_addr_t plat_map_dma_mem(struct device *dev, void *addr, size_t size) |
| 56 | { |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 57 | return bmips_phys_to_dma(dev, virt_to_phys(addr)); |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | dma_addr_t plat_map_dma_mem_page(struct device *dev, struct page *page) |
| 61 | { |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 62 | return bmips_phys_to_dma(dev, page_to_phys(page)); |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | unsigned long plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr) |
| 66 | { |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 67 | struct bmips_dma_range *r; |
| 68 | |
| 69 | for (r = bmips_dma_ranges; r && r->size; r++) { |
| 70 | if (dma_addr >= r->parent_addr && |
| 71 | dma_addr < (r->parent_addr + r->size)) |
| 72 | return dma_addr - r->parent_addr + r->child_addr; |
| 73 | } |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 74 | return dma_addr; |
| 75 | } |
| 76 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 77 | static int __init bmips_init_dma_ranges(void) |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 78 | { |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 79 | struct device_node *np = |
| 80 | of_find_compatible_node(NULL, NULL, "brcm,ubus"); |
| 81 | const __be32 *data; |
| 82 | struct bmips_dma_range *r; |
| 83 | int len; |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 84 | |
| 85 | if (!np) |
| 86 | return 0; |
| 87 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 88 | data = of_get_property(np, "dma-ranges", &len); |
| 89 | if (!data) |
| 90 | goto out_good; |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 91 | |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 92 | len /= sizeof(*data) * 3; |
| 93 | if (!len) |
| 94 | goto out_bad; |
| 95 | |
| 96 | /* add a dummy (zero) entry at the end as a sentinel */ |
| 97 | bmips_dma_ranges = kzalloc(sizeof(struct bmips_dma_range) * (len + 1), |
| 98 | GFP_KERNEL); |
| 99 | if (!bmips_dma_ranges) |
| 100 | goto out_bad; |
| 101 | |
| 102 | for (r = bmips_dma_ranges; len; len--, r++) { |
| 103 | r->child_addr = be32_to_cpup(data++); |
| 104 | r->parent_addr = be32_to_cpup(data++); |
| 105 | r->size = be32_to_cpup(data++); |
| 106 | } |
| 107 | |
| 108 | out_good: |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 109 | of_node_put(np); |
| 110 | return 0; |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 111 | |
| 112 | out_bad: |
| 113 | pr_err("error parsing dma-ranges property\n"); |
| 114 | of_node_put(np); |
| 115 | return -EINVAL; |
Kevin Cernekee | d666cd0 | 2014-10-20 21:28:05 -0700 | [diff] [blame] | 116 | } |
Kevin Cernekee | 1ada656 | 2014-12-25 09:49:12 -0800 | [diff] [blame] | 117 | arch_initcall(bmips_init_dma_ranges); |