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