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

Configure Feed

Select the types of activity you want to include in your feed.

at 55fa6091d83160ca772fc37cebae45d42695a708 314 lines 6.5 kB view raw
1/* 2 * driver/dma/coh901318_lli.c 3 * 4 * Copyright (C) 2007-2009 ST-Ericsson 5 * License terms: GNU General Public License (GPL) version 2 6 * Support functions for handling lli for dma 7 * Author: Per Friden <per.friden@stericsson.com> 8 */ 9 10#include <linux/dma-mapping.h> 11#include <linux/spinlock.h> 12#include <linux/dmapool.h> 13#include <linux/memory.h> 14#include <linux/gfp.h> 15#include <mach/coh901318.h> 16 17#include "coh901318_lli.h" 18 19#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) 20#define DEBUGFS_POOL_COUNTER_RESET(pool) (pool->debugfs_pool_counter = 0) 21#define DEBUGFS_POOL_COUNTER_ADD(pool, add) (pool->debugfs_pool_counter += add) 22#else 23#define DEBUGFS_POOL_COUNTER_RESET(pool) 24#define DEBUGFS_POOL_COUNTER_ADD(pool, add) 25#endif 26 27static struct coh901318_lli * 28coh901318_lli_next(struct coh901318_lli *data) 29{ 30 if (data == NULL || data->link_addr == 0) 31 return NULL; 32 33 return (struct coh901318_lli *) data->virt_link_addr; 34} 35 36int coh901318_pool_create(struct coh901318_pool *pool, 37 struct device *dev, 38 size_t size, size_t align) 39{ 40 spin_lock_init(&pool->lock); 41 pool->dev = dev; 42 pool->dmapool = dma_pool_create("lli_pool", dev, size, align, 0); 43 44 DEBUGFS_POOL_COUNTER_RESET(pool); 45 return 0; 46} 47 48int coh901318_pool_destroy(struct coh901318_pool *pool) 49{ 50 51 dma_pool_destroy(pool->dmapool); 52 return 0; 53} 54 55struct coh901318_lli * 56coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len) 57{ 58 int i; 59 struct coh901318_lli *head; 60 struct coh901318_lli *lli; 61 struct coh901318_lli *lli_prev; 62 dma_addr_t phy; 63 64 if (len == 0) 65 goto err; 66 67 spin_lock(&pool->lock); 68 69 head = dma_pool_alloc(pool->dmapool, GFP_NOWAIT, &phy); 70 71 if (head == NULL) 72 goto err; 73 74 DEBUGFS_POOL_COUNTER_ADD(pool, 1); 75 76 lli = head; 77 lli->phy_this = phy; 78 lli->link_addr = 0x00000000; 79 lli->virt_link_addr = 0x00000000U; 80 81 for (i = 1; i < len; i++) { 82 lli_prev = lli; 83 84 lli = dma_pool_alloc(pool->dmapool, GFP_NOWAIT, &phy); 85 86 if (lli == NULL) 87 goto err_clean_up; 88 89 DEBUGFS_POOL_COUNTER_ADD(pool, 1); 90 lli->phy_this = phy; 91 lli->link_addr = 0x00000000; 92 lli->virt_link_addr = 0x00000000U; 93 94 lli_prev->link_addr = phy; 95 lli_prev->virt_link_addr = lli; 96 } 97 98 spin_unlock(&pool->lock); 99 100 return head; 101 102 err: 103 spin_unlock(&pool->lock); 104 return NULL; 105 106 err_clean_up: 107 lli_prev->link_addr = 0x00000000U; 108 spin_unlock(&pool->lock); 109 coh901318_lli_free(pool, &head); 110 return NULL; 111} 112 113void coh901318_lli_free(struct coh901318_pool *pool, 114 struct coh901318_lli **lli) 115{ 116 struct coh901318_lli *l; 117 struct coh901318_lli *next; 118 119 if (lli == NULL) 120 return; 121 122 l = *lli; 123 124 if (l == NULL) 125 return; 126 127 spin_lock(&pool->lock); 128 129 while (l->link_addr) { 130 next = l->virt_link_addr; 131 dma_pool_free(pool->dmapool, l, l->phy_this); 132 DEBUGFS_POOL_COUNTER_ADD(pool, -1); 133 l = next; 134 } 135 dma_pool_free(pool->dmapool, l, l->phy_this); 136 DEBUGFS_POOL_COUNTER_ADD(pool, -1); 137 138 spin_unlock(&pool->lock); 139 *lli = NULL; 140} 141 142int 143coh901318_lli_fill_memcpy(struct coh901318_pool *pool, 144 struct coh901318_lli *lli, 145 dma_addr_t source, unsigned int size, 146 dma_addr_t destination, u32 ctrl_chained, 147 u32 ctrl_eom) 148{ 149 int s = size; 150 dma_addr_t src = source; 151 dma_addr_t dst = destination; 152 153 lli->src_addr = src; 154 lli->dst_addr = dst; 155 156 while (lli->link_addr) { 157 lli->control = ctrl_chained | MAX_DMA_PACKET_SIZE; 158 lli->src_addr = src; 159 lli->dst_addr = dst; 160 161 s -= MAX_DMA_PACKET_SIZE; 162 lli = coh901318_lli_next(lli); 163 164 src += MAX_DMA_PACKET_SIZE; 165 dst += MAX_DMA_PACKET_SIZE; 166 } 167 168 lli->control = ctrl_eom | s; 169 lli->src_addr = src; 170 lli->dst_addr = dst; 171 172 return 0; 173} 174 175int 176coh901318_lli_fill_single(struct coh901318_pool *pool, 177 struct coh901318_lli *lli, 178 dma_addr_t buf, unsigned int size, 179 dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl_eom, 180 enum dma_data_direction dir) 181{ 182 int s = size; 183 dma_addr_t src; 184 dma_addr_t dst; 185 186 187 if (dir == DMA_TO_DEVICE) { 188 src = buf; 189 dst = dev_addr; 190 191 } else if (dir == DMA_FROM_DEVICE) { 192 193 src = dev_addr; 194 dst = buf; 195 } else { 196 return -EINVAL; 197 } 198 199 while (lli->link_addr) { 200 size_t block_size = MAX_DMA_PACKET_SIZE; 201 lli->control = ctrl_chained | MAX_DMA_PACKET_SIZE; 202 203 /* If we are on the next-to-final block and there will 204 * be less than half a DMA packet left for the last 205 * block, then we want to make this block a little 206 * smaller to balance the sizes. This is meant to 207 * avoid too small transfers if the buffer size is 208 * (MAX_DMA_PACKET_SIZE*N + 1) */ 209 if (s < (MAX_DMA_PACKET_SIZE + MAX_DMA_PACKET_SIZE/2)) 210 block_size = MAX_DMA_PACKET_SIZE/2; 211 212 s -= block_size; 213 lli->src_addr = src; 214 lli->dst_addr = dst; 215 216 lli = coh901318_lli_next(lli); 217 218 if (dir == DMA_TO_DEVICE) 219 src += block_size; 220 else if (dir == DMA_FROM_DEVICE) 221 dst += block_size; 222 } 223 224 lli->control = ctrl_eom | s; 225 lli->src_addr = src; 226 lli->dst_addr = dst; 227 228 return 0; 229} 230 231int 232coh901318_lli_fill_sg(struct coh901318_pool *pool, 233 struct coh901318_lli *lli, 234 struct scatterlist *sgl, unsigned int nents, 235 dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl, 236 u32 ctrl_last, 237 enum dma_data_direction dir, u32 ctrl_irq_mask) 238{ 239 int i; 240 struct scatterlist *sg; 241 u32 ctrl_sg; 242 dma_addr_t src = 0; 243 dma_addr_t dst = 0; 244 u32 bytes_to_transfer; 245 u32 elem_size; 246 247 if (lli == NULL) 248 goto err; 249 250 spin_lock(&pool->lock); 251 252 if (dir == DMA_TO_DEVICE) 253 dst = dev_addr; 254 else if (dir == DMA_FROM_DEVICE) 255 src = dev_addr; 256 else 257 goto err; 258 259 for_each_sg(sgl, sg, nents, i) { 260 if (sg_is_chain(sg)) { 261 /* sg continues to the next sg-element don't 262 * send ctrl_finish until the last 263 * sg-element in the chain 264 */ 265 ctrl_sg = ctrl_chained; 266 } else if (i == nents - 1) 267 ctrl_sg = ctrl_last; 268 else 269 ctrl_sg = ctrl ? ctrl : ctrl_last; 270 271 272 if (dir == DMA_TO_DEVICE) 273 /* increment source address */ 274 src = sg_phys(sg); 275 else 276 /* increment destination address */ 277 dst = sg_phys(sg); 278 279 bytes_to_transfer = sg_dma_len(sg); 280 281 while (bytes_to_transfer) { 282 u32 val; 283 284 if (bytes_to_transfer > MAX_DMA_PACKET_SIZE) { 285 elem_size = MAX_DMA_PACKET_SIZE; 286 val = ctrl_chained; 287 } else { 288 elem_size = bytes_to_transfer; 289 val = ctrl_sg; 290 } 291 292 lli->control = val | elem_size; 293 lli->src_addr = src; 294 lli->dst_addr = dst; 295 296 if (dir == DMA_FROM_DEVICE) 297 dst += elem_size; 298 else 299 src += elem_size; 300 301 BUG_ON(lli->link_addr & 3); 302 303 bytes_to_transfer -= elem_size; 304 lli = coh901318_lli_next(lli); 305 } 306 307 } 308 spin_unlock(&pool->lock); 309 310 return 0; 311 err: 312 spin_unlock(&pool->lock); 313 return -EINVAL; 314}