0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/slab.h>
0010 #include <linux/list.h>
0011 #include <linux/bug.h>
0012
0013 #include "dma_fifo.h"
0014
0015 #ifdef DEBUG_TRACING
0016 #define df_trace(s, args...) pr_debug(s, ##args)
0017 #else
0018 #define df_trace(s, args...)
0019 #endif
0020
0021 #define FAIL(fifo, condition, format...) ({ \
0022 fifo->corrupt = !!(condition); \
0023 WARN(fifo->corrupt, format); \
0024 })
0025
0026
0027
0028
0029 static bool addr_check(unsigned int check, unsigned int lo, unsigned int hi)
0030 {
0031 return check - (lo + 1) < (hi - 1) - lo;
0032 }
0033
0034
0035
0036
0037
0038 void dma_fifo_init(struct dma_fifo *fifo)
0039 {
0040 memset(fifo, 0, sizeof(*fifo));
0041 INIT_LIST_HEAD(&fifo->pending);
0042 }
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned int align,
0059 int tx_limit, int open_limit, gfp_t gfp_mask)
0060 {
0061 int capacity;
0062
0063 if (!is_power_of_2(align) || size < 0)
0064 return -EINVAL;
0065
0066 size = round_up(size, align);
0067 capacity = size + align * open_limit + align * DMA_FIFO_GUARD;
0068 fifo->data = kmalloc(capacity, gfp_mask);
0069 if (!fifo->data)
0070 return -ENOMEM;
0071
0072 fifo->in = 0;
0073 fifo->out = 0;
0074 fifo->done = 0;
0075 fifo->size = size;
0076 fifo->avail = size;
0077 fifo->align = align;
0078 fifo->tx_limit = max_t(int, round_down(tx_limit, align), align);
0079 fifo->open = 0;
0080 fifo->open_limit = open_limit;
0081 fifo->guard = size + align * open_limit;
0082 fifo->capacity = capacity;
0083 fifo->corrupt = 0;
0084
0085 return 0;
0086 }
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096 void dma_fifo_free(struct dma_fifo *fifo)
0097 {
0098 struct dma_pending *pending, *next;
0099
0100 if (!fifo->data)
0101 return;
0102
0103 list_for_each_entry_safe(pending, next, &fifo->pending, link)
0104 list_del_init(&pending->link);
0105 kfree(fifo->data);
0106 fifo->data = NULL;
0107 }
0108
0109
0110
0111
0112
0113 void dma_fifo_reset(struct dma_fifo *fifo)
0114 {
0115 struct dma_pending *pending, *next;
0116
0117 if (!fifo->data)
0118 return;
0119
0120 list_for_each_entry_safe(pending, next, &fifo->pending, link)
0121 list_del_init(&pending->link);
0122 fifo->in = 0;
0123 fifo->out = 0;
0124 fifo->done = 0;
0125 fifo->avail = fifo->size;
0126 fifo->open = 0;
0127 fifo->corrupt = 0;
0128 }
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139 int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n)
0140 {
0141 int ofs, l;
0142
0143 if (!fifo->data)
0144 return -ENOENT;
0145 if (fifo->corrupt)
0146 return -ENXIO;
0147
0148 if (n > fifo->avail)
0149 n = fifo->avail;
0150 if (n <= 0)
0151 return 0;
0152
0153 ofs = fifo->in % fifo->capacity;
0154 l = min(n, fifo->capacity - ofs);
0155 memcpy(fifo->data + ofs, src, l);
0156 memcpy(fifo->data, src + l, n - l);
0157
0158 if (FAIL(fifo, addr_check(fifo->done, fifo->in, fifo->in + n) ||
0159 fifo->avail < n,
0160 "fifo corrupt: in:%u out:%u done:%u n:%d avail:%d",
0161 fifo->in, fifo->out, fifo->done, n, fifo->avail))
0162 return -ENXIO;
0163
0164 fifo->in += n;
0165 fifo->avail -= n;
0166
0167 df_trace("in:%u out:%u done:%u n:%d avail:%d", fifo->in, fifo->out,
0168 fifo->done, n, fifo->avail);
0169
0170 return n;
0171 }
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182 int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended)
0183 {
0184 unsigned int len, n, ofs, l, limit;
0185
0186 if (!fifo->data)
0187 return -ENOENT;
0188 if (fifo->corrupt)
0189 return -ENXIO;
0190
0191 pended->len = 0;
0192 pended->data = NULL;
0193 pended->out = fifo->out;
0194
0195 len = fifo->in - fifo->out;
0196 if (!len)
0197 return -ENODATA;
0198 if (fifo->open == fifo->open_limit)
0199 return -EAGAIN;
0200
0201 n = len;
0202 ofs = fifo->out % fifo->capacity;
0203 l = fifo->capacity - ofs;
0204 limit = min_t(unsigned int, l, fifo->tx_limit);
0205 if (n > limit) {
0206 n = limit;
0207 fifo->out += limit;
0208 } else if (ofs + n > fifo->guard) {
0209 fifo->out += l;
0210 fifo->in = fifo->out;
0211 } else {
0212 fifo->out += round_up(n, fifo->align);
0213 fifo->in = fifo->out;
0214 }
0215
0216 df_trace("in: %u out: %u done: %u n: %d len: %u avail: %d", fifo->in,
0217 fifo->out, fifo->done, n, len, fifo->avail);
0218
0219 pended->len = n;
0220 pended->data = fifo->data + ofs;
0221 pended->next = fifo->out;
0222 list_add_tail(&pended->link, &fifo->pending);
0223 ++fifo->open;
0224
0225 if (FAIL(fifo, fifo->open > fifo->open_limit,
0226 "past open limit:%d (limit:%d)",
0227 fifo->open, fifo->open_limit))
0228 return -ENXIO;
0229 if (FAIL(fifo, fifo->out & (fifo->align - 1),
0230 "fifo out unaligned:%u (align:%u)",
0231 fifo->out, fifo->align))
0232 return -ENXIO;
0233
0234 return len - n;
0235 }
0236
0237
0238
0239
0240
0241
0242 int dma_fifo_out_complete(struct dma_fifo *fifo, struct dma_pending *complete)
0243 {
0244 struct dma_pending *pending, *next, *tmp;
0245
0246 if (!fifo->data)
0247 return -ENOENT;
0248 if (fifo->corrupt)
0249 return -ENXIO;
0250 if (list_empty(&fifo->pending) && fifo->open == 0)
0251 return -EINVAL;
0252
0253 if (FAIL(fifo, list_empty(&fifo->pending) != (fifo->open == 0),
0254 "pending list disagrees with open count:%d",
0255 fifo->open))
0256 return -ENXIO;
0257
0258 tmp = complete->data;
0259 *tmp = *complete;
0260 list_replace(&complete->link, &tmp->link);
0261 dp_mark_completed(tmp);
0262
0263
0264 list_for_each_entry_safe(pending, next, &fifo->pending, link) {
0265 if (!dp_is_completed(pending)) {
0266 df_trace("still pending: saved out: %u len: %d",
0267 pending->out, pending->len);
0268 break;
0269 }
0270
0271 if (FAIL(fifo, pending->out != fifo->done ||
0272 addr_check(fifo->in, fifo->done, pending->next),
0273 "in:%u out:%u done:%u saved:%u next:%u",
0274 fifo->in, fifo->out, fifo->done, pending->out,
0275 pending->next))
0276 return -ENXIO;
0277
0278 list_del_init(&pending->link);
0279 fifo->done = pending->next;
0280 fifo->avail += pending->len;
0281 --fifo->open;
0282
0283 df_trace("in: %u out: %u done: %u len: %u avail: %d", fifo->in,
0284 fifo->out, fifo->done, pending->len, fifo->avail);
0285 }
0286
0287 if (FAIL(fifo, fifo->open < 0, "open dma:%d < 0", fifo->open))
0288 return -ENXIO;
0289 if (FAIL(fifo, fifo->avail > fifo->size, "fifo avail:%d > size:%d",
0290 fifo->avail, fifo->size))
0291 return -ENXIO;
0292
0293 return 0;
0294 }