/* $Id: at91pdcvar.h,v 1.3 2012/11/12 18:00:36 skrll Exp $ */ #ifndef _AT91PDCVAR_H_ #define _AT91PDCVAR_H_ #include #include #if UNTESTED typedef struct at91pdc_buf { void *buf_arg; /* argument (mbuf or other data) */ int buf_len; /* length of data sent / recv */ bus_dmamap_t buf_dmamap; /* dma map */ } at91pdc_buf_t; typedef struct at91pdc_queue { at91pdc_buf_t q_buf[2]; /* two buffers */ unsigned q_ndx; /* buffer being sent (if q_len > 0) */ unsigned q_len; /* number of buffers being used (<= 2) */ } at91pdc_queue_t; #define AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free) do { \ unsigned _i = (_pq)->q_ndx % 2; \ at91pdc_buf_t *_buf = &(_pq)->q_buf[i]; \ void *_arg = _buf->buf_arg; \ \ if (_sync) { \ bus_dmamap_sync((_dmat), buf->buf_dmamap, 0, \ buf->buf_len, (_sync)); \ } \ bus_dmamap_unload((_dmat), buf->buf_dmamap); \ buf->buf_arg = 0; buf->buf_len = 0; \ \ (_pq)->q_ndx = i ^ 1; \ (_pq)->q_len--; \ \ (_free)(_arg); \ } while (/*CONSTCOND*/0) #define AT91PDC_UNLOAD_QUEUE(_pq, _dmat, _sync, _free, _idle) \ do { \ if ((_pq)->q_len > 1) \ AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free); \ if ((_idle) && (_pq)->q_len > 0) \ AT91PDC_UNLOAD_BUF(_pq, _dmat, _sync, _free); \ } while (/*CONSTCOND*/0) #endif /* UNTESTED */ typedef struct at91pdc_fifo { bus_dmamap_t f_dmamap; /* DMA map */ void *f_buf; /* buffer address */ int f_buf_size; /* size of the fifo */ int f_ndx; /* current read/write index */ int f_length; /* number of bytes in fifo */ bus_addr_t f_buf_addr; /* buffer bus addr */ int f_pdc_rd_ndx; /* PDC read index */ int f_pdc_wr_ndx; /* PDC write index */ int f_pdc_space; /* number of bytes allocated for pdc */ } at91pdc_fifo_t; static __inline int AT91PDC_FIFO_EMPTY(at91pdc_fifo_t *fifo) { return fifo->f_length == 0; } static __inline int AT91PDC_FIFO_FULL(at91pdc_fifo_t *fifo) { return fifo->f_length >= fifo->f_buf_size; } static __inline int AT91PDC_FIFO_SPACE(at91pdc_fifo_t *fifo) { return fifo->f_buf_size - fifo->f_length; } static __inline void AT91PDC_RESET_FIFO(bus_space_tag_t iot, bus_space_handle_t ioh, bus_dma_tag_t dmat, uint offset, at91pdc_fifo_t *fifo, int rw) { fifo->f_ndx = fifo->f_length = 0; fifo->f_pdc_rd_ndx = fifo->f_pdc_wr_ndx = 0; fifo->f_pdc_space = fifo->f_buf_size; if (!rw) { bus_space_write_4(iot, ioh, offset + PDC_RNCR, 0); bus_space_write_4(iot, ioh, offset + PDC_RCR, 0); bus_space_write_4(iot, ioh, offset + PDC_RNPR, fifo->f_buf_addr); bus_space_write_4(iot, ioh, offset + PDC_RPR, fifo->f_buf_addr); } else { bus_space_write_4(iot, ioh, offset + PDC_TNCR, 0); bus_space_write_4(iot, ioh, offset + PDC_TCR, 0); bus_space_write_4(iot, ioh, offset + PDC_TNPR, fifo->f_buf_addr); bus_space_write_4(iot, ioh, offset + PDC_TPR, fifo->f_buf_addr); } } static __inline int AT91PDC_FIFO_PREREAD(bus_space_tag_t iot, bus_space_handle_t ioh, bus_dma_tag_t dmat, uint offset, at91pdc_fifo_t *fifo, uint chunk_size) { int al; int ret = 1; /* then check if we can queue new block */ if (bus_space_read_4(iot, ioh, offset + PDC_RNCR)) goto get_out; if (fifo->f_pdc_space < chunk_size) { ret = 0; goto get_out; } /* fifo has enough space left for next chunk! */ bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_wr_ndx, chunk_size, BUS_DMASYNC_PREREAD); bus_space_write_4(iot, ioh, offset + PDC_RNPR, fifo->f_buf_addr + fifo->f_pdc_wr_ndx); bus_space_write_4(iot, ioh, offset + PDC_RNCR, chunk_size); if ((fifo->f_pdc_wr_ndx += chunk_size) >= fifo->f_buf_size) fifo->f_pdc_wr_ndx = 0; fifo->f_pdc_space -= chunk_size; get_out: /* now check if we need to re-synchronize last read chunk too */ al = fifo->f_pdc_rd_ndx % chunk_size; if (al) { bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, chunk_size - al, BUS_DMASYNC_PREREAD); } return ret; } static __inline void AT91PDC_FIFO_POSTREAD(bus_space_tag_t iot, bus_space_handle_t ioh, bus_dma_tag_t dmat, uint offset, at91pdc_fifo_t *fifo) { uint32_t pdc_ptr = bus_space_read_4(iot, ioh, offset + PDC_RPR); int32_t cc = pdc_ptr - fifo->f_buf_addr - fifo->f_pdc_rd_ndx; /* handle fifo wrapping: */ if (cc < 0) { cc = fifo->f_buf_size - fifo->f_pdc_rd_ndx; if (cc > 0) { bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, cc, BUS_DMASYNC_POSTREAD); fifo->f_length += cc; fifo->f_pdc_rd_ndx += cc; } fifo->f_pdc_rd_ndx = 0; cc = pdc_ptr - fifo->f_buf_addr; } if (cc > 0) { /* data has been received! */ bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, cc, BUS_DMASYNC_POSTREAD); fifo->f_length += cc; fifo->f_pdc_rd_ndx += cc; } } static __inline void *AT91PDC_FIFO_RDPTR(at91pdc_fifo_t *fifo, int *num_bytes) { if (fifo->f_length <= 0) { return NULL; } int contig_bytes = fifo->f_buf_size - fifo->f_ndx; if (contig_bytes > fifo->f_length) contig_bytes = fifo->f_length; *num_bytes = contig_bytes; return (void*)((uintptr_t)fifo->f_buf + fifo->f_ndx); } static __inline void AT91PDC_FIFO_READ(at91pdc_fifo_t *fifo, int bytes_read) { if (bytes_read > fifo->f_length) bytes_read = fifo->f_length; int contig_bytes = fifo->f_buf_size - fifo->f_ndx; fifo->f_length -= bytes_read; fifo->f_pdc_space += bytes_read; if (bytes_read < contig_bytes) fifo->f_ndx += bytes_read; else fifo->f_ndx = bytes_read - contig_bytes; } static __inline int AT91PDC_FIFO_PREWRITE(bus_space_tag_t iot, bus_space_handle_t ioh, bus_dma_tag_t dmat, uint offset, at91pdc_fifo_t *fifo, uint max_chunk_size) { if (bus_space_read_4(iot, ioh, offset + PDC_TNCR) != 0) return 1; int len = fifo->f_buf_size - fifo->f_pdc_rd_ndx; int max_len = fifo->f_length - (fifo->f_buf_size - fifo->f_pdc_space); if (len > max_len) len = max_len; if (len > max_chunk_size) len = max_chunk_size; if (len > fifo->f_pdc_space) panic("%s: len %d > pdc_space (f_length=%d space=%d size=%d)", __FUNCTION__, len, fifo->f_length, fifo->f_pdc_space, fifo->f_buf_size); if (len == 0) return 0; if (len < 0) panic("%s: len < 0 (f_length=%d space=%d size=%d)", __FUNCTION__, fifo->f_length, fifo->f_pdc_space, fifo->f_buf_size); /* there's something to write */ bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_rd_ndx, len, BUS_DMASYNC_PREWRITE); bus_space_write_4(iot, ioh, offset + PDC_TNPR, fifo->f_buf_addr + fifo->f_pdc_rd_ndx); bus_space_write_4(iot, ioh, offset + PDC_TNCR, len); if ((fifo->f_pdc_rd_ndx += len) >= fifo->f_buf_size) fifo->f_pdc_rd_ndx = 0; fifo->f_pdc_space -= len; return 1; } static __inline void AT91PDC_FIFO_POSTWRITE(bus_space_tag_t iot, bus_space_handle_t ioh, bus_dma_tag_t dmat, uint offset, at91pdc_fifo_t *fifo) { uint32_t pdc_ptr = bus_space_read_4(iot, ioh, offset + PDC_TPR); int32_t cc = pdc_ptr - fifo->f_buf_addr - fifo->f_pdc_wr_ndx; /* handle fifo wrapping: */ if (cc < 0) { cc = fifo->f_buf_size - fifo->f_pdc_wr_ndx; if (cc > 0) { bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_wr_ndx, cc, BUS_DMASYNC_POSTWRITE); fifo->f_length -= cc; fifo->f_pdc_space += cc; } fifo->f_pdc_wr_ndx = 0; cc = pdc_ptr - fifo->f_buf_addr; } if (cc > 0) { /* data has been sent! */ bus_dmamap_sync(dmat, fifo->f_dmamap, fifo->f_pdc_wr_ndx, cc, BUS_DMASYNC_POSTWRITE); fifo->f_length -= cc; fifo->f_pdc_space += cc; fifo->f_pdc_wr_ndx += cc; } } static __inline void *AT91PDC_FIFO_WRPTR(at91pdc_fifo_t *fifo, int *max_bytes) { int space = fifo->f_buf_size - fifo->f_length; if (space <= 0) return NULL; int contig_bytes = fifo->f_buf_size - fifo->f_ndx; if (contig_bytes > space) contig_bytes = space; *max_bytes = contig_bytes; return (void*)((uintptr_t)fifo->f_buf + fifo->f_ndx); } static __inline void AT91PDC_FIFO_WRITTEN(at91pdc_fifo_t *fifo, int bytes_written) { if (bytes_written > (fifo->f_buf_size - fifo->f_length)) bytes_written = (fifo->f_buf_size - fifo->f_length); int contig_bytes = fifo->f_buf_size - fifo->f_ndx; fifo->f_length += bytes_written; if (bytes_written < contig_bytes) fifo->f_ndx += bytes_written; else fifo->f_ndx = bytes_written - contig_bytes; } int at91pdc_alloc_fifo(bus_dma_tag_t dmat, at91pdc_fifo_t *fifo, int size, int flags); #endif // !_AT91PDCVAR_H_