Peter Hurley | e571107 | 2012-11-02 08:16:33 -0400 | [diff] [blame] | 1 | /* |
| 2 | * DMA-able FIFO interface |
| 3 | * |
| 4 | * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com> |
| 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 as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Peter Hurley | e571107 | 2012-11-02 08:16:33 -0400 | [diff] [blame] | 15 | */ |
| 16 | |
| 17 | #ifndef _DMA_FIFO_H_ |
| 18 | #define _DMA_FIFO_H_ |
| 19 | |
| 20 | /** |
| 21 | * The design basis for the DMA FIFO is to provide an output side that |
| 22 | * complies with the streaming DMA API design that can be DMA'd from directly |
| 23 | * (without additional copying), coupled with an input side that maintains a |
| 24 | * logically consistent 'apparent' size (ie, bytes in + bytes avail is static |
| 25 | * for the lifetime of the FIFO). |
| 26 | * |
| 27 | * DMA output transactions originate on a cache line boundary and can be |
| 28 | * variably-sized. DMA output transactions can be retired out-of-order but |
| 29 | * the FIFO will only advance the output in the original input sequence. |
| 30 | * This means the FIFO will eventually stall if a transaction is never retired. |
| 31 | * |
| 32 | * Chunking the output side into cache line multiples means that some FIFO |
| 33 | * memory is unused. For example, if all the avail input has been pended out, |
| 34 | * then the in and out markers are re-aligned to the next cache line. |
| 35 | * The maximum possible waste is |
| 36 | * (cache line alignment - 1) * (max outstanding dma transactions) |
| 37 | * This potential waste requires additional hidden capacity within the FIFO |
| 38 | * to be able to accept input while the 'apparent' size has not been reached. |
| 39 | * |
| 40 | * Additional cache lines (ie, guard area) are used to minimize DMA |
| 41 | * fragmentation when wrapping at the end of the FIFO. Input is allowed into the |
| 42 | * guard area, but the in and out FIFO markers are wrapped when DMA is pended. |
| 43 | */ |
| 44 | |
| 45 | #define DMA_FIFO_GUARD 3 /* # of cache lines to reserve for the guard area */ |
| 46 | |
| 47 | struct dma_fifo { |
| 48 | unsigned in; |
| 49 | unsigned out; /* updated when dma is pended */ |
| 50 | unsigned done; /* updated upon dma completion */ |
| 51 | struct { |
| 52 | unsigned corrupt:1; |
| 53 | }; |
| 54 | int size; /* 'apparent' size of fifo */ |
| 55 | int guard; /* ofs of guard area */ |
| 56 | int capacity; /* size + reserved */ |
| 57 | int avail; /* # of unused bytes in fifo */ |
| 58 | unsigned align; /* must be power of 2 */ |
| 59 | int tx_limit; /* max # of bytes per dma transaction */ |
| 60 | int open_limit; /* max # of outstanding allowed */ |
| 61 | int open; /* # of outstanding dma transactions */ |
| 62 | struct list_head pending; /* fifo markers for outstanding dma */ |
| 63 | void *data; |
| 64 | }; |
| 65 | |
| 66 | struct dma_pending { |
| 67 | struct list_head link; |
| 68 | void *data; |
| 69 | unsigned len; |
| 70 | unsigned next; |
| 71 | unsigned out; |
| 72 | }; |
| 73 | |
| 74 | static inline void dp_mark_completed(struct dma_pending *dp) |
| 75 | { |
| 76 | dp->data += 1; |
| 77 | } |
| 78 | |
| 79 | static inline bool dp_is_completed(struct dma_pending *dp) |
| 80 | { |
| 81 | return (unsigned long)dp->data & 1UL; |
| 82 | } |
| 83 | |
Dominique van den Broeck | d949210 | 2014-04-12 15:18:14 +0200 | [diff] [blame] | 84 | void dma_fifo_init(struct dma_fifo *fifo); |
| 85 | int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align, |
| 86 | int tx_limit, int open_limit, gfp_t gfp_mask); |
| 87 | void dma_fifo_free(struct dma_fifo *fifo); |
| 88 | void dma_fifo_reset(struct dma_fifo *fifo); |
| 89 | int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n); |
| 90 | int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended); |
| 91 | int dma_fifo_out_complete(struct dma_fifo *fifo, |
| 92 | struct dma_pending *complete); |
Peter Hurley | e571107 | 2012-11-02 08:16:33 -0400 | [diff] [blame] | 93 | |
| 94 | /* returns the # of used bytes in the fifo */ |
| 95 | static inline int dma_fifo_level(struct dma_fifo *fifo) |
| 96 | { |
| 97 | return fifo->size - fifo->avail; |
| 98 | } |
| 99 | |
| 100 | /* returns the # of bytes ready for output in the fifo */ |
| 101 | static inline int dma_fifo_out_level(struct dma_fifo *fifo) |
| 102 | { |
| 103 | return fifo->in - fifo->out; |
| 104 | } |
| 105 | |
| 106 | /* returns the # of unused bytes in the fifo */ |
| 107 | static inline int dma_fifo_avail(struct dma_fifo *fifo) |
| 108 | { |
| 109 | return fifo->avail; |
| 110 | } |
| 111 | |
| 112 | /* returns true if fifo has max # of outstanding dmas */ |
| 113 | static inline bool dma_fifo_busy(struct dma_fifo *fifo) |
| 114 | { |
| 115 | return fifo->open == fifo->open_limit; |
| 116 | } |
| 117 | |
| 118 | /* changes the max size of dma returned from dma_fifo_out_pend() */ |
| 119 | static inline int dma_fifo_change_tx_limit(struct dma_fifo *fifo, int tx_limit) |
| 120 | { |
| 121 | tx_limit = round_down(tx_limit, fifo->align); |
| 122 | fifo->tx_limit = max_t(int, tx_limit, fifo->align); |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | #endif /* _DMA_FIFO_H_ */ |