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