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