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