• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #ifndef _DMA_FIFO_H_
22 #define _DMA_FIFO_H_
23 
24 /**
25  * The design basis for the DMA FIFO is to provide an output side that
26  * complies with the streaming DMA API design that can be DMA'd from directly
27  * (without additional copying), coupled with an input side that maintains a
28  * logically consistent 'apparent' size (ie, bytes in + bytes avail is static
29  * for the lifetime of the FIFO).
30  *
31  * DMA output transactions originate on a cache line boundary and can be
32  * variably-sized. DMA output transactions can be retired out-of-order but
33  * the FIFO will only advance the output in the original input sequence.
34  * This means the FIFO will eventually stall if a transaction is never retired.
35  *
36  * Chunking the output side into cache line multiples means that some FIFO
37  * memory is unused. For example, if all the avail input has been pended out,
38  * then the in and out markers are re-aligned to the next cache line.
39  * The maximum possible waste is
40  *     (cache line alignment - 1) * (max outstanding dma transactions)
41  * This potential waste requires additional hidden capacity within the FIFO
42  * to be able to accept input while the 'apparent' size has not been reached.
43  *
44  * Additional cache lines (ie, guard area) are used to minimize DMA
45  * fragmentation when wrapping at the end of the FIFO. Input is allowed into the
46  * guard area, but the in and out FIFO markers are wrapped when DMA is pended.
47  */
48 
49 #define DMA_FIFO_GUARD 3   /* # of cache lines to reserve for the guard area */
50 
51 struct dma_fifo {
52 	unsigned	 in;
53 	unsigned	 out;		/* updated when dma is pended         */
54 	unsigned	 done;		/* updated upon dma completion        */
55 	struct {
56 		unsigned corrupt:1;
57 	};
58 	int		 size;		/* 'apparent' size of fifo	      */
59 	int		 guard;		/* ofs of guard area		      */
60 	int		 capacity;	/* size + reserved                    */
61 	int		 avail;		/* # of unused bytes in fifo          */
62 	unsigned	 align;		/* must be power of 2                 */
63 	int		 tx_limit;	/* max # of bytes per dma transaction */
64 	int		 open_limit;	/* max # of outstanding allowed       */
65 	int		 open;		/* # of outstanding dma transactions  */
66 	struct list_head pending;	/* fifo markers for outstanding dma   */
67 	void		 *data;
68 };
69 
70 struct dma_pending {
71 	struct list_head link;
72 	void		 *data;
73 	unsigned	 len;
74 	unsigned         next;
75 	unsigned         out;
76 };
77 
dp_mark_completed(struct dma_pending * dp)78 static inline void dp_mark_completed(struct dma_pending *dp)
79 {
80 	dp->data += 1;
81 }
82 
dp_is_completed(struct dma_pending * dp)83 static inline bool dp_is_completed(struct dma_pending *dp)
84 {
85 	return (unsigned long)dp->data & 1UL;
86 }
87 
88 extern void dma_fifo_init(struct dma_fifo *fifo);
89 extern int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align,
90 			  int tx_limit, int open_limit, gfp_t gfp_mask);
91 extern void dma_fifo_free(struct dma_fifo *fifo);
92 extern void dma_fifo_reset(struct dma_fifo *fifo);
93 extern int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n);
94 extern int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended);
95 extern int dma_fifo_out_complete(struct dma_fifo *fifo,
96 				 struct dma_pending *complete);
97 
98 /* returns the # of used bytes in the fifo */
dma_fifo_level(struct dma_fifo * fifo)99 static inline int dma_fifo_level(struct dma_fifo *fifo)
100 {
101 	return fifo->size - fifo->avail;
102 }
103 
104 /* returns the # of bytes ready for output in the fifo */
dma_fifo_out_level(struct dma_fifo * fifo)105 static inline int dma_fifo_out_level(struct dma_fifo *fifo)
106 {
107 	return fifo->in - fifo->out;
108 }
109 
110 /* returns the # of unused bytes in the fifo */
dma_fifo_avail(struct dma_fifo * fifo)111 static inline int dma_fifo_avail(struct dma_fifo *fifo)
112 {
113 	return fifo->avail;
114 }
115 
116 /* returns true if fifo has max # of outstanding dmas */
dma_fifo_busy(struct dma_fifo * fifo)117 static inline bool dma_fifo_busy(struct dma_fifo *fifo)
118 {
119 	return fifo->open == fifo->open_limit;
120 }
121 
122 /* changes the max size of dma returned from dma_fifo_out_pend() */
dma_fifo_change_tx_limit(struct dma_fifo * fifo,int tx_limit)123 static inline int dma_fifo_change_tx_limit(struct dma_fifo *fifo, int tx_limit)
124 {
125 	tx_limit = round_down(tx_limit, fifo->align);
126 	fifo->tx_limit = max_t(int, tx_limit, fifo->align);
127 	return 0;
128 }
129 
130 #endif /* _DMA_FIFO_H_ */
131