Home | History | Annotate | Line # | Download | only in at91
      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 
     11 typedef 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 
     17 typedef 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)	\
     42 do {								\
     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 
     52 typedef 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 
     65 static __inline int AT91PDC_FIFO_EMPTY(at91pdc_fifo_t *fifo)
     66 {
     67 	return fifo->f_length == 0;
     68 }
     69 
     70 static __inline int AT91PDC_FIFO_FULL(at91pdc_fifo_t *fifo)
     71 {
     72 	return fifo->f_length >= fifo->f_buf_size;
     73 }
     74 
     75 static __inline int AT91PDC_FIFO_SPACE(at91pdc_fifo_t *fifo)
     76 {
     77 	return fifo->f_buf_size - fifo->f_length;
     78 }
     79 
     80 
     81 static __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 
    106 static __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;
    134 get_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 
    147 static __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 
    178 static __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 
    190 static __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 
    203 static __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 
    242 static __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 
    274 static __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 
    286 static __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 
    298 int at91pdc_alloc_fifo(bus_dma_tag_t dmat, at91pdc_fifo_t *fifo, int size, int flags);
    299 
    300 #endif	// !_AT91PDCVAR_H_
    301