root/drivers/staging/fwserial/dma_fifo.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. addr_check
  2. dma_fifo_init
  3. dma_fifo_alloc
  4. dma_fifo_free
  5. dma_fifo_reset
  6. dma_fifo_in
  7. dma_fifo_out_pend
  8. dma_fifo_out_complete

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * DMA-able FIFO implementation
   4  *
   5  * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com>
   6  */
   7 
   8 #include <linux/kernel.h>
   9 #include <linux/slab.h>
  10 #include <linux/list.h>
  11 #include <linux/bug.h>
  12 
  13 #include "dma_fifo.h"
  14 
  15 #ifdef DEBUG_TRACING
  16 #define df_trace(s, args...) pr_debug(s, ##args)
  17 #else
  18 #define df_trace(s, args...)
  19 #endif
  20 
  21 #define FAIL(fifo, condition, format...) ({                             \
  22         fifo->corrupt = !!(condition);                                  \
  23         WARN(fifo->corrupt, format);                                    \
  24 })
  25 
  26 /*
  27  * private helper fn to determine if check is in open interval (lo,hi)
  28  */
  29 static bool addr_check(unsigned int check, unsigned int lo, unsigned int hi)
  30 {
  31         return check - (lo + 1) < (hi - 1) - lo;
  32 }
  33 
  34 /**
  35  * dma_fifo_init: initialize the fifo to a valid but inoperative state
  36  * @fifo: address of in-place "struct dma_fifo" object
  37  */
  38 void dma_fifo_init(struct dma_fifo *fifo)
  39 {
  40         memset(fifo, 0, sizeof(*fifo));
  41         INIT_LIST_HEAD(&fifo->pending);
  42 }
  43 
  44 /**
  45  * dma_fifo_alloc - initialize and allocate dma_fifo
  46  * @fifo: address of in-place "struct dma_fifo" object
  47  * @size: 'apparent' size, in bytes, of fifo
  48  * @align: dma alignment to maintain (should be at least cpu cache alignment),
  49  *         must be power of 2
  50  * @tx_limit: maximum # of bytes transmissible per dma (rounded down to
  51  *            multiple of alignment, but at least align size)
  52  * @open_limit: maximum # of outstanding dma transactions allowed
  53  * @gfp_mask: get_free_pages mask, passed to kmalloc()
  54  *
  55  * The 'apparent' size will be rounded up to next greater aligned size.
  56  * Returns 0 if no error, otherwise an error code
  57  */
  58 int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned int align,
  59                    int tx_limit, int open_limit, gfp_t gfp_mask)
  60 {
  61         int capacity;
  62 
  63         if (!is_power_of_2(align) || size < 0)
  64                 return -EINVAL;
  65 
  66         size = round_up(size, align);
  67         capacity = size + align * open_limit + align * DMA_FIFO_GUARD;
  68         fifo->data = kmalloc(capacity, gfp_mask);
  69         if (!fifo->data)
  70                 return -ENOMEM;
  71 
  72         fifo->in = 0;
  73         fifo->out = 0;
  74         fifo->done = 0;
  75         fifo->size = size;
  76         fifo->avail = size;
  77         fifo->align = align;
  78         fifo->tx_limit = max_t(int, round_down(tx_limit, align), align);
  79         fifo->open = 0;
  80         fifo->open_limit = open_limit;
  81         fifo->guard = size + align * open_limit;
  82         fifo->capacity = capacity;
  83         fifo->corrupt = 0;
  84 
  85         return 0;
  86 }
  87 
  88 /**
  89  * dma_fifo_free - frees the fifo
  90  * @fifo: address of in-place "struct dma_fifo" to free
  91  *
  92  * Also reinits the fifo to a valid but inoperative state. This
  93  * allows the fifo to be reused with a different target requiring
  94  * different fifo parameters.
  95  */
  96 void dma_fifo_free(struct dma_fifo *fifo)
  97 {
  98         struct dma_pending *pending, *next;
  99 
 100         if (!fifo->data)
 101                 return;
 102 
 103         list_for_each_entry_safe(pending, next, &fifo->pending, link)
 104                 list_del_init(&pending->link);
 105         kfree(fifo->data);
 106         fifo->data = NULL;
 107 }
 108 
 109 /**
 110  * dma_fifo_reset - dumps the fifo contents and reinits for reuse
 111  * @fifo: address of in-place "struct dma_fifo" to reset
 112  */
 113 void dma_fifo_reset(struct dma_fifo *fifo)
 114 {
 115         struct dma_pending *pending, *next;
 116 
 117         if (!fifo->data)
 118                 return;
 119 
 120         list_for_each_entry_safe(pending, next, &fifo->pending, link)
 121                 list_del_init(&pending->link);
 122         fifo->in = 0;
 123         fifo->out = 0;
 124         fifo->done = 0;
 125         fifo->avail = fifo->size;
 126         fifo->open = 0;
 127         fifo->corrupt = 0;
 128 }
 129 
 130 /**
 131  * dma_fifo_in - copies data into the fifo
 132  * @fifo: address of in-place "struct dma_fifo" to write to
 133  * @src: buffer to copy from
 134  * @n: # of bytes to copy
 135  *
 136  * Returns the # of bytes actually copied, which can be less than requested if
 137  * the fifo becomes full. If < 0, return is error code.
 138  */
 139 int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n)
 140 {
 141         int ofs, l;
 142 
 143         if (!fifo->data)
 144                 return -ENOENT;
 145         if (fifo->corrupt)
 146                 return -ENXIO;
 147 
 148         if (n > fifo->avail)
 149                 n = fifo->avail;
 150         if (n <= 0)
 151                 return 0;
 152 
 153         ofs = fifo->in % fifo->capacity;
 154         l = min(n, fifo->capacity - ofs);
 155         memcpy(fifo->data + ofs, src, l);
 156         memcpy(fifo->data, src + l, n - l);
 157 
 158         if (FAIL(fifo, addr_check(fifo->done, fifo->in, fifo->in + n) ||
 159                  fifo->avail < n,
 160                  "fifo corrupt: in:%u out:%u done:%u n:%d avail:%d",
 161                  fifo->in, fifo->out, fifo->done, n, fifo->avail))
 162                 return -ENXIO;
 163 
 164         fifo->in += n;
 165         fifo->avail -= n;
 166 
 167         df_trace("in:%u out:%u done:%u n:%d avail:%d", fifo->in, fifo->out,
 168                  fifo->done, n, fifo->avail);
 169 
 170         return n;
 171 }
 172 
 173 /**
 174  * dma_fifo_out_pend - gets address/len of next avail read and marks as pended
 175  * @fifo: address of in-place "struct dma_fifo" to read from
 176  * @pended: address of structure to fill with read address/len
 177  *          The data/len fields will be NULL/0 if no dma is pended.
 178  *
 179  * Returns the # of used bytes remaining in fifo (ie, if > 0, more data
 180  * remains in the fifo that was not pended). If < 0, return is error code.
 181  */
 182 int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended)
 183 {
 184         unsigned int len, n, ofs, l, limit;
 185 
 186         if (!fifo->data)
 187                 return -ENOENT;
 188         if (fifo->corrupt)
 189                 return -ENXIO;
 190 
 191         pended->len = 0;
 192         pended->data = NULL;
 193         pended->out = fifo->out;
 194 
 195         len = fifo->in - fifo->out;
 196         if (!len)
 197                 return -ENODATA;
 198         if (fifo->open == fifo->open_limit)
 199                 return -EAGAIN;
 200 
 201         n = len;
 202         ofs = fifo->out % fifo->capacity;
 203         l = fifo->capacity - ofs;
 204         limit = min_t(unsigned int, l, fifo->tx_limit);
 205         if (n > limit) {
 206                 n = limit;
 207                 fifo->out += limit;
 208         } else if (ofs + n > fifo->guard) {
 209                 fifo->out += l;
 210                 fifo->in = fifo->out;
 211         } else {
 212                 fifo->out += round_up(n, fifo->align);
 213                 fifo->in = fifo->out;
 214         }
 215 
 216         df_trace("in: %u out: %u done: %u n: %d len: %u avail: %d", fifo->in,
 217                  fifo->out, fifo->done, n, len, fifo->avail);
 218 
 219         pended->len = n;
 220         pended->data = fifo->data + ofs;
 221         pended->next = fifo->out;
 222         list_add_tail(&pended->link, &fifo->pending);
 223         ++fifo->open;
 224 
 225         if (FAIL(fifo, fifo->open > fifo->open_limit,
 226                  "past open limit:%d (limit:%d)",
 227                  fifo->open, fifo->open_limit))
 228                 return -ENXIO;
 229         if (FAIL(fifo, fifo->out & (fifo->align - 1),
 230                  "fifo out unaligned:%u (align:%u)",
 231                  fifo->out, fifo->align))
 232                 return -ENXIO;
 233 
 234         return len - n;
 235 }
 236 
 237 /**
 238  * dma_fifo_out_complete - marks pended dma as completed
 239  * @fifo: address of in-place "struct dma_fifo" which was read from
 240  * @complete: address of structure for previously pended dma to mark completed
 241  */
 242 int dma_fifo_out_complete(struct dma_fifo *fifo, struct dma_pending *complete)
 243 {
 244         struct dma_pending *pending, *next, *tmp;
 245 
 246         if (!fifo->data)
 247                 return -ENOENT;
 248         if (fifo->corrupt)
 249                 return -ENXIO;
 250         if (list_empty(&fifo->pending) && fifo->open == 0)
 251                 return -EINVAL;
 252 
 253         if (FAIL(fifo, list_empty(&fifo->pending) != (fifo->open == 0),
 254                  "pending list disagrees with open count:%d",
 255                  fifo->open))
 256                 return -ENXIO;
 257 
 258         tmp = complete->data;
 259         *tmp = *complete;
 260         list_replace(&complete->link, &tmp->link);
 261         dp_mark_completed(tmp);
 262 
 263         /* Only update the fifo in the original pended order */
 264         list_for_each_entry_safe(pending, next, &fifo->pending, link) {
 265                 if (!dp_is_completed(pending)) {
 266                         df_trace("still pending: saved out: %u len: %d",
 267                                  pending->out, pending->len);
 268                         break;
 269                 }
 270 
 271                 if (FAIL(fifo, pending->out != fifo->done ||
 272                          addr_check(fifo->in, fifo->done, pending->next),
 273                          "in:%u out:%u done:%u saved:%u next:%u",
 274                          fifo->in, fifo->out, fifo->done, pending->out,
 275                          pending->next))
 276                         return -ENXIO;
 277 
 278                 list_del_init(&pending->link);
 279                 fifo->done = pending->next;
 280                 fifo->avail += pending->len;
 281                 --fifo->open;
 282 
 283                 df_trace("in: %u out: %u done: %u len: %u avail: %d", fifo->in,
 284                          fifo->out, fifo->done, pending->len, fifo->avail);
 285         }
 286 
 287         if (FAIL(fifo, fifo->open < 0, "open dma:%d < 0", fifo->open))
 288                 return -ENXIO;
 289         if (FAIL(fifo, fifo->avail > fifo->size, "fifo avail:%d > size:%d",
 290                  fifo->avail, fifo->size))
 291                 return -ENXIO;
 292 
 293         return 0;
 294 }

/* [<][>][^][v][top][bottom][index][help] */