1/*	$Id: at91pdcvar.h,v 1.3 2012/11/12 18:00:36 skrll Exp $	*/
2
3#ifndef	_AT91PDCVAR_H_
4#define	_AT91PDCVAR_H_
5
6#include <arm/at91/at91pdcreg.h>
7#include <sys/param.h>
8
9#if	UNTESTED
10
11typedef struct at91pdc_buf {
12	void			*buf_arg;	/* argument (mbuf or other data) */
13	int			buf_len;	/* length of data sent / recv */
14	bus_dmamap_t		buf_dmamap;	/* dma map */
15} at91pdc_buf_t;
16
17typedef struct at91pdc_queue {
18	at91pdc_buf_t		q_buf[2];	/* two buffers */
19	unsigned		q_ndx;		/* buffer being sent (if q_len > 0) */
20	unsigned		q_len;		/* number of buffers being used (<= 2) */
21} at91pdc_queue_t;
22
23#define	AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free) do {	\
24	unsigned	_i = (_pq)->q_ndx % 2;			\
25	at91pdc_buf_t	*_buf = &(_pq)->q_buf[i];		\
26	void		*_arg = _buf->buf_arg;			\
27								\
28	if (_sync) {						\
29		bus_dmamap_sync((_dmat), buf->buf_dmamap, 0,	\
30				buf->buf_len, (_sync));		\
31	}							\
32	bus_dmamap_unload((_dmat), buf->buf_dmamap);		\
33	buf->buf_arg = 0; buf->buf_len = 0;			\
34								\
35	(_pq)->q_ndx = i ^ 1;					\
36	(_pq)->q_len--;						\
37								\
38	(_free)(_arg);						\
39} while (/*CONSTCOND*/0)
40
41#define	AT91PDC_UNLOAD_QUEUE(_pq, _dmat, _sync, _free, _idle)	\
42do {								\
43	if ((_pq)->q_len > 1)					\
44		AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free);	\
45	if ((_idle) && (_pq)->q_len > 0)			\
46		AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free);	\
47} while (/*CONSTCOND*/0)
48
49#endif	/* UNTESTED */
50
51
52typedef struct at91pdc_fifo {
53	bus_dmamap_t	f_dmamap;	/* DMA map			*/
54	void		*f_buf;		/* buffer address		*/
55	int		f_buf_size;	/* size of the fifo		*/
56	int		f_ndx;		/* current read/write index	*/
57	int		f_length;	/* number of bytes in fifo	*/
58
59	bus_addr_t	f_buf_addr;	/* buffer bus addr		*/
60	int		f_pdc_rd_ndx;	/* PDC read index		*/
61	int		f_pdc_wr_ndx;	/* PDC write index		*/
62	int		f_pdc_space;	/* number of bytes allocated for pdc */
63} at91pdc_fifo_t;
64
65static __inline int AT91PDC_FIFO_EMPTY(at91pdc_fifo_t *fifo)
66{
67	return fifo->f_length == 0;
68}
69
70static __inline int AT91PDC_FIFO_FULL(at91pdc_fifo_t *fifo)
71{
72	return fifo->f_length >= fifo->f_buf_size;
73}
74
75static __inline int AT91PDC_FIFO_SPACE(at91pdc_fifo_t *fifo)
76{
77	return fifo->f_buf_size - fifo->f_length;
78}
79
80
81static __inline void AT91PDC_RESET_FIFO(bus_space_tag_t iot,
82					 bus_space_handle_t ioh,
83					 bus_dma_tag_t dmat,
84					 uint offset,
85					 at91pdc_fifo_t *fifo,
86					 int rw)
87{
88	fifo->f_ndx = fifo->f_length = 0;
89
90	fifo->f_pdc_rd_ndx = fifo->f_pdc_wr_ndx = 0;
91	fifo->f_pdc_space = fifo->f_buf_size;
92
93	if (!rw) {
94		bus_space_write_4(iot, ioh, offset + PDC_RNCR, 0);
95		bus_space_write_4(iot, ioh, offset + PDC_RCR, 0);
96		bus_space_write_4(iot, ioh, offset + PDC_RNPR, fifo->f_buf_addr);
97		bus_space_write_4(iot, ioh, offset + PDC_RPR, fifo->f_buf_addr);
98	} else {
99		bus_space_write_4(iot, ioh, offset + PDC_TNCR, 0);
100		bus_space_write_4(iot, ioh, offset + PDC_TCR, 0);
101		bus_space_write_4(iot, ioh, offset + PDC_TNPR, fifo->f_buf_addr);
102		bus_space_write_4(iot, ioh, offset + PDC_TPR, fifo->f_buf_addr);
103	}
104}
105
106static __inline int AT91PDC_FIFO_PREREAD(bus_space_tag_t iot,
107					  bus_space_handle_t ioh,
108					  bus_dma_tag_t dmat,
109					  uint offset,
110					  at91pdc_fifo_t *fifo,
111					  uint chunk_size)
112{
113	int al;
114	int ret = 1;
115
116	/* then check if we can queue new block */
117	if (bus_space_read_4(iot, ioh, offset + PDC_RNCR))
118		goto get_out;
119	if (fifo->f_pdc_space < chunk_size) {
120		ret = 0;
121		goto get_out;
122	}
123	/* fifo has enough space left for next chunk! */
124	bus_dmamap_sync(dmat,
125			fifo->f_dmamap,
126			fifo->f_pdc_wr_ndx,
127			chunk_size,
128			BUS_DMASYNC_PREREAD);
129	bus_space_write_4(iot, ioh, offset + PDC_RNPR, fifo->f_buf_addr + fifo->f_pdc_wr_ndx);
130	bus_space_write_4(iot, ioh, offset + PDC_RNCR, chunk_size);
131	if ((fifo->f_pdc_wr_ndx += chunk_size) >= fifo->f_buf_size)
132		fifo->f_pdc_wr_ndx = 0;
133	fifo->f_pdc_space -= chunk_size;
134get_out:
135	/* now check if we need to re-synchronize last read chunk too */
136	al = fifo->f_pdc_rd_ndx % chunk_size;
137	if (al) {
138		bus_dmamap_sync(dmat,
139				fifo->f_dmamap,
140				fifo->f_pdc_rd_ndx,
141				chunk_size - al,
142				BUS_DMASYNC_PREREAD);
143	}
144	return ret;
145}
146
147static __inline void AT91PDC_FIFO_POSTREAD(bus_space_tag_t iot,
148					    bus_space_handle_t ioh,
149					    bus_dma_tag_t dmat,
150					    uint offset,
151					    at91pdc_fifo_t *fifo)
152{
153	uint32_t	pdc_ptr = bus_space_read_4(iot, ioh, offset + PDC_RPR);
154	int32_t		cc = pdc_ptr - fifo->f_buf_addr - fifo->f_pdc_rd_ndx;
155
156	/* handle fifo wrapping: */
157	if (cc < 0) {
158		cc = fifo->f_buf_size - fifo->f_pdc_rd_ndx;
159		if (cc > 0) {
160			bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, cc,
161					BUS_DMASYNC_POSTREAD);
162			fifo->f_length += cc;
163			fifo->f_pdc_rd_ndx += cc;
164		}
165		fifo->f_pdc_rd_ndx = 0;
166		cc = pdc_ptr - fifo->f_buf_addr;
167	}
168
169	if (cc > 0) {
170		/* data has been received! */
171		bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, cc,
172				BUS_DMASYNC_POSTREAD);
173		fifo->f_length += cc;
174		fifo->f_pdc_rd_ndx += cc;
175	}
176}
177
178static __inline void *AT91PDC_FIFO_RDPTR(at91pdc_fifo_t *fifo, int *num_bytes)
179{
180	if (fifo->f_length <= 0) {
181		return NULL;
182	}
183	int contig_bytes = fifo->f_buf_size - fifo->f_ndx;
184	if (contig_bytes > fifo->f_length)
185		contig_bytes = fifo->f_length;
186	*num_bytes = contig_bytes;
187	return (void*)((uintptr_t)fifo->f_buf + fifo->f_ndx);
188}
189
190static __inline void AT91PDC_FIFO_READ(at91pdc_fifo_t *fifo, int bytes_read)
191{
192	if (bytes_read > fifo->f_length)
193		bytes_read = fifo->f_length;
194	int contig_bytes = fifo->f_buf_size - fifo->f_ndx;
195	fifo->f_length -= bytes_read;
196	fifo->f_pdc_space += bytes_read;
197	if (bytes_read < contig_bytes)
198		fifo->f_ndx += bytes_read;
199	else
200		fifo->f_ndx = bytes_read - contig_bytes;
201}
202
203static __inline int AT91PDC_FIFO_PREWRITE(bus_space_tag_t iot,
204					   bus_space_handle_t ioh,
205					   bus_dma_tag_t dmat,
206					   uint offset,
207					   at91pdc_fifo_t *fifo,
208					   uint max_chunk_size)
209{
210	if (bus_space_read_4(iot, ioh, offset + PDC_TNCR) != 0)
211		return 1;
212	int len = fifo->f_buf_size - fifo->f_pdc_rd_ndx;
213	int max_len = fifo->f_length - (fifo->f_buf_size - fifo->f_pdc_space);
214	if (len > max_len)
215		len = max_len;
216	if (len > max_chunk_size)
217		len = max_chunk_size;
218	if (len > fifo->f_pdc_space)
219		panic("%s: len %d > pdc_space (f_length=%d space=%d size=%d)",
220		      __FUNCTION__, len, fifo->f_length, fifo->f_pdc_space, fifo->f_buf_size);
221	if (len == 0)
222		return 0;
223	if (len < 0)
224		panic("%s: len < 0 (f_length=%d space=%d size=%d)",
225		      __FUNCTION__, fifo->f_length, fifo->f_pdc_space, fifo->f_buf_size);
226
227	/* there's something to write */
228	bus_dmamap_sync(dmat,
229			fifo->f_dmamap,
230			fifo->f_pdc_rd_ndx,
231			len,
232			BUS_DMASYNC_PREWRITE);
233	bus_space_write_4(iot, ioh, offset + PDC_TNPR, fifo->f_buf_addr + fifo->f_pdc_rd_ndx);
234	bus_space_write_4(iot, ioh, offset + PDC_TNCR, len);
235	if ((fifo->f_pdc_rd_ndx += len) >= fifo->f_buf_size)
236		fifo->f_pdc_rd_ndx = 0;
237	fifo->f_pdc_space -= len;
238
239	return 1;
240}
241
242static __inline void AT91PDC_FIFO_POSTWRITE(bus_space_tag_t iot,
243					     bus_space_handle_t ioh,
244					     bus_dma_tag_t dmat,
245					     uint offset,
246					     at91pdc_fifo_t *fifo)
247{
248	uint32_t	pdc_ptr = bus_space_read_4(iot, ioh, offset + PDC_TPR);
249	int32_t		cc = pdc_ptr - fifo->f_buf_addr - fifo->f_pdc_wr_ndx;
250
251	/* handle fifo wrapping: */
252	if (cc < 0) {
253		cc = fifo->f_buf_size - fifo->f_pdc_wr_ndx;
254		if (cc > 0) {
255			bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_wr_ndx, cc,
256					BUS_DMASYNC_POSTWRITE);
257			fifo->f_length -= cc;
258			fifo->f_pdc_space += cc;
259		}
260		fifo->f_pdc_wr_ndx = 0;
261		cc = pdc_ptr - fifo->f_buf_addr;
262	}
263
264	if (cc > 0) {
265		/* data has been sent! */
266		bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_wr_ndx, cc,
267				BUS_DMASYNC_POSTWRITE);
268		fifo->f_length -= cc;
269		fifo->f_pdc_space += cc;
270		fifo->f_pdc_wr_ndx += cc;
271	}
272}
273
274static __inline void *AT91PDC_FIFO_WRPTR(at91pdc_fifo_t *fifo, int *max_bytes)
275{
276	int space = fifo->f_buf_size - fifo->f_length;
277	if (space <= 0)
278		return NULL;
279	int contig_bytes = fifo->f_buf_size - fifo->f_ndx;
280	if (contig_bytes > space)
281		contig_bytes = space;
282	*max_bytes = contig_bytes;
283	return (void*)((uintptr_t)fifo->f_buf + fifo->f_ndx);
284}
285
286static __inline void AT91PDC_FIFO_WRITTEN(at91pdc_fifo_t *fifo, int bytes_written)
287{
288	if (bytes_written > (fifo->f_buf_size - fifo->f_length))
289		bytes_written = (fifo->f_buf_size - fifo->f_length);
290	int contig_bytes = fifo->f_buf_size - fifo->f_ndx;
291	fifo->f_length += bytes_written;
292	if (bytes_written < contig_bytes)
293		fifo->f_ndx += bytes_written;
294	else
295		fifo->f_ndx = bytes_written - contig_bytes;
296}
297
298int at91pdc_alloc_fifo(bus_dma_tag_t dmat, at91pdc_fifo_t *fifo, int size, int flags);
299
300#endif	// !_AT91PDCVAR_H_
301