Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Hexagon: Provide DMA implementation

Signed-off-by: Richard Kuo <rkuo@codeaurora.org>
Signed-off-by: Linas Vepstas <linas@codeaurora.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Richard Kuo and committed by
Linus Torvalds
65c3d7c5 5df87c15

+350
+101
arch/hexagon/include/asm/dma-mapping.h
··· 1 + /* 2 + * DMA operations for the Hexagon architecture 3 + * 4 + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 and 8 + * only version 2 as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program; if not, write to the Free Software 17 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 + * 02110-1301, USA. 19 + */ 20 + 21 + #ifndef _ASM_DMA_MAPPING_H 22 + #define _ASM_DMA_MAPPING_H 23 + 24 + #include <linux/types.h> 25 + #include <linux/cache.h> 26 + #include <linux/mm.h> 27 + #include <linux/scatterlist.h> 28 + #include <linux/dma-mapping.h> 29 + #include <linux/dma-debug.h> 30 + #include <linux/dma-attrs.h> 31 + #include <asm/io.h> 32 + 33 + struct device; 34 + extern int bad_dma_address; 35 + 36 + extern struct dma_map_ops *dma_ops; 37 + 38 + #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) 39 + #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) 40 + 41 + static inline struct dma_map_ops *get_dma_ops(struct device *dev) 42 + { 43 + if (unlikely(dev == NULL)) 44 + return NULL; 45 + 46 + return dma_ops; 47 + } 48 + 49 + extern int dma_supported(struct device *dev, u64 mask); 50 + extern int dma_set_mask(struct device *dev, u64 mask); 51 + extern int dma_is_consistent(struct device *dev, dma_addr_t dma_handle); 52 + extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, 53 + enum dma_data_direction direction); 54 + 55 + #include <asm-generic/dma-mapping-common.h> 56 + 57 + static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) 58 + { 59 + if (!dev->dma_mask) 60 + return 0; 61 + return addr + size - 1 <= *dev->dma_mask; 62 + } 63 + 64 + static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) 65 + { 66 + struct dma_map_ops *dma_ops = get_dma_ops(dev); 67 + 68 + if (dma_ops->mapping_error) 69 + return dma_ops->mapping_error(dev, dma_addr); 70 + 71 + return (dma_addr == bad_dma_address); 72 + } 73 + 74 + static inline void *dma_alloc_coherent(struct device *dev, size_t size, 75 + dma_addr_t *dma_handle, gfp_t flag) 76 + { 77 + void *ret; 78 + struct dma_map_ops *ops = get_dma_ops(dev); 79 + 80 + BUG_ON(!dma_ops); 81 + 82 + ret = ops->alloc_coherent(dev, size, dma_handle, flag); 83 + 84 + debug_dma_alloc_coherent(dev, size, *dma_handle, ret); 85 + 86 + return ret; 87 + } 88 + 89 + static inline void dma_free_coherent(struct device *dev, size_t size, 90 + void *cpu_addr, dma_addr_t dma_handle) 91 + { 92 + struct dma_map_ops *dma_ops = get_dma_ops(dev); 93 + 94 + BUG_ON(!dma_ops); 95 + 96 + dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); 97 + 98 + debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); 99 + } 100 + 101 + #endif
+29
arch/hexagon/include/asm/dma.h
··· 1 + /* 2 + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 and 6 + * only version 2 as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public License 14 + * along with this program; if not, write to the Free Software 15 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 16 + * 02110-1301, USA. 17 + */ 18 + 19 + #ifndef _ASM_DMA_H 20 + #define _ASM_DMA_H 21 + 22 + #include <asm/io.h> 23 + 24 + #define MAX_DMA_CHANNELS 1 25 + #define MAX_DMA_ADDRESS (PAGE_OFFSET) 26 + 27 + extern size_t hexagon_coherent_pool_size; 28 + 29 + #endif
+220
arch/hexagon/kernel/dma.c
··· 1 + /* 2 + * DMA implementation for Hexagon 3 + * 4 + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 and 8 + * only version 2 as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program; if not, write to the Free Software 17 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 + * 02110-1301, USA. 19 + */ 20 + 21 + #include <linux/dma-mapping.h> 22 + #include <linux/bootmem.h> 23 + #include <linux/genalloc.h> 24 + #include <asm/dma-mapping.h> 25 + 26 + struct dma_map_ops *dma_ops; 27 + EXPORT_SYMBOL(dma_ops); 28 + 29 + int bad_dma_address; /* globals are automatically initialized to zero */ 30 + 31 + int dma_supported(struct device *dev, u64 mask) 32 + { 33 + if (mask == DMA_BIT_MASK(32)) 34 + return 1; 35 + else 36 + return 0; 37 + } 38 + EXPORT_SYMBOL(dma_supported); 39 + 40 + int dma_set_mask(struct device *dev, u64 mask) 41 + { 42 + if (!dev->dma_mask || !dma_supported(dev, mask)) 43 + return -EIO; 44 + 45 + *dev->dma_mask = mask; 46 + 47 + return 0; 48 + } 49 + EXPORT_SYMBOL(dma_set_mask); 50 + 51 + static struct gen_pool *coherent_pool; 52 + 53 + 54 + /* Allocates from a pool of uncached memory that was reserved at boot time */ 55 + 56 + void *hexagon_dma_alloc_coherent(struct device *dev, size_t size, 57 + dma_addr_t *dma_addr, gfp_t flag) 58 + { 59 + void *ret; 60 + 61 + if (coherent_pool == NULL) { 62 + coherent_pool = gen_pool_create(PAGE_SHIFT, -1); 63 + 64 + if (coherent_pool == NULL) 65 + panic("Can't create %s() memory pool!", __func__); 66 + else 67 + gen_pool_add(coherent_pool, 68 + (PAGE_OFFSET + (max_low_pfn << PAGE_SHIFT)), 69 + hexagon_coherent_pool_size, -1); 70 + } 71 + 72 + ret = (void *) gen_pool_alloc(coherent_pool, size); 73 + 74 + if (ret) { 75 + memset(ret, 0, size); 76 + *dma_addr = (dma_addr_t) (ret - PAGE_OFFSET); 77 + } else 78 + *dma_addr = ~0; 79 + 80 + return ret; 81 + } 82 + 83 + static void hexagon_free_coherent(struct device *dev, size_t size, void *vaddr, 84 + dma_addr_t dma_addr) 85 + { 86 + gen_pool_free(coherent_pool, (unsigned long) vaddr, size); 87 + } 88 + 89 + static int check_addr(const char *name, struct device *hwdev, 90 + dma_addr_t bus, size_t size) 91 + { 92 + if (hwdev && hwdev->dma_mask && !dma_capable(hwdev, bus, size)) { 93 + if (*hwdev->dma_mask >= DMA_BIT_MASK(32)) 94 + printk(KERN_ERR 95 + "%s: overflow %Lx+%zu of device mask %Lx\n", 96 + name, (long long)bus, size, 97 + (long long)*hwdev->dma_mask); 98 + return 0; 99 + } 100 + return 1; 101 + } 102 + 103 + static int hexagon_map_sg(struct device *hwdev, struct scatterlist *sg, 104 + int nents, enum dma_data_direction dir, 105 + struct dma_attrs *attrs) 106 + { 107 + struct scatterlist *s; 108 + int i; 109 + 110 + WARN_ON(nents == 0 || sg[0].length == 0); 111 + 112 + for_each_sg(sg, s, nents, i) { 113 + s->dma_address = sg_phys(s); 114 + if (!check_addr("map_sg", hwdev, s->dma_address, s->length)) 115 + return 0; 116 + 117 + s->dma_length = s->length; 118 + 119 + flush_dcache_range(PAGE_OFFSET + s->dma_address, 120 + PAGE_OFFSET + s->dma_address + s->length); 121 + } 122 + 123 + return nents; 124 + } 125 + 126 + /* 127 + * address is virtual 128 + */ 129 + static inline void dma_sync(void *addr, size_t size, 130 + enum dma_data_direction dir) 131 + { 132 + switch (dir) { 133 + case DMA_TO_DEVICE: 134 + hexagon_clean_dcache_range((unsigned long) addr, 135 + (unsigned long) addr + size); 136 + break; 137 + case DMA_FROM_DEVICE: 138 + hexagon_inv_dcache_range((unsigned long) addr, 139 + (unsigned long) addr + size); 140 + break; 141 + case DMA_BIDIRECTIONAL: 142 + flush_dcache_range((unsigned long) addr, 143 + (unsigned long) addr + size); 144 + break; 145 + default: 146 + BUG(); 147 + } 148 + } 149 + 150 + static inline void *dma_addr_to_virt(dma_addr_t dma_addr) 151 + { 152 + return phys_to_virt((unsigned long) dma_addr); 153 + } 154 + 155 + /** 156 + * hexagon_map_page() - maps an address for device DMA 157 + * @dev: pointer to DMA device 158 + * @page: pointer to page struct of DMA memory 159 + * @offset: offset within page 160 + * @size: size of memory to map 161 + * @dir: transfer direction 162 + * @attrs: pointer to DMA attrs (not used) 163 + * 164 + * Called to map a memory address to a DMA address prior 165 + * to accesses to/from device. 166 + * 167 + * We don't particularly have many hoops to jump through 168 + * so far. Straight translation between phys and virtual. 169 + * 170 + * DMA is not cache coherent so sync is necessary; this 171 + * seems to be a convenient place to do it. 172 + * 173 + */ 174 + static dma_addr_t hexagon_map_page(struct device *dev, struct page *page, 175 + unsigned long offset, size_t size, 176 + enum dma_data_direction dir, 177 + struct dma_attrs *attrs) 178 + { 179 + dma_addr_t bus = page_to_phys(page) + offset; 180 + WARN_ON(size == 0); 181 + 182 + if (!check_addr("map_single", dev, bus, size)) 183 + return bad_dma_address; 184 + 185 + dma_sync(dma_addr_to_virt(bus), size, dir); 186 + 187 + return bus; 188 + } 189 + 190 + static void hexagon_sync_single_for_cpu(struct device *dev, 191 + dma_addr_t dma_handle, size_t size, 192 + enum dma_data_direction dir) 193 + { 194 + dma_sync(dma_addr_to_virt(dma_handle), size, dir); 195 + } 196 + 197 + static void hexagon_sync_single_for_device(struct device *dev, 198 + dma_addr_t dma_handle, size_t size, 199 + enum dma_data_direction dir) 200 + { 201 + dma_sync(dma_addr_to_virt(dma_handle), size, dir); 202 + } 203 + 204 + struct dma_map_ops hexagon_dma_ops = { 205 + .alloc_coherent = hexagon_dma_alloc_coherent, 206 + .free_coherent = hexagon_free_coherent, 207 + .map_sg = hexagon_map_sg, 208 + .map_page = hexagon_map_page, 209 + .sync_single_for_cpu = hexagon_sync_single_for_cpu, 210 + .sync_single_for_device = hexagon_sync_single_for_device, 211 + .is_phys = 1, 212 + }; 213 + 214 + void __init hexagon_dma_init(void) 215 + { 216 + if (dma_ops) 217 + return; 218 + 219 + dma_ops = &hexagon_dma_ops; 220 + }