shithub: p9-stm32-example-bare

ref: 91f14160d425ce557a3fc589b9286fd82c56b7a8
dir: /dma.c/

View raw version
#include	<u.h>
#include	"dat.h"
#include	"fns.h"
#include	"include/stm32f103xb.h"
#include	"libkern/kern.h"
#include	"debug.h"

#define ISR_TCIF(c)		(1 << ((c << 2) + 1))
#define	CHANBUF_NULL	0xF00

typedef
struct Chanbuf {
	// TODO locking
	short	buf[DMA_BUFSIZE];
	uint	index, ltc;
} Chanbuf;

static Chanbuf *dma_chbufs[6]	=	{ nil };

void	dma_ch6init(void);
void	dma_ch7init(void);

/*	DMA init & enable	*/
void
dmainit()
{
	dma_ch6init();
	// dma_ch7init();
}

void
dmaen()
{
	// enable appropriate channels
	DMA1_Channel6->CCR |= DMA_CCR_EN;
	//! DMA1_Channel7->CCR |= DMA_CCR_EN;

}

/*	DMA general purpose functions	*/
void
dma_write(DmaChannel c, void *buf, ulong n)
{
	DBGDMAWR print("dmawr%d: write: %uld bytes\n", c + 1, n);

	DMA_Channel_TypeDef *dma = nil;

	switch(c) {
	case DmaChan1:	dma = DMA1_Channel1; break;
	case DmaChan2:	dma = DMA1_Channel2; break;
	case DmaChan3:	dma = DMA1_Channel3; break;
	case DmaChan4:	dma = DMA1_Channel4; break;
	case DmaChan5:	dma = DMA1_Channel5; break;
	case DmaChan6:	dma = DMA1_Channel6; break;
	case DmaChan7:	dma = DMA1_Channel7; break;
	default:		panic("dma: cannot write to DMA channel %d\n", c + 1); return;
	}

	while(n) {
		DBGDMAWR print("dmawr%d: %uld bytes left\n", c + 1, n);

		while(dma->CCR & DMA_CCR_EN != 0 && (DMA1->ISR & ISR_TCIF(c)) == 0);
		dma->CCR &= ~DMA_CCR_EN;
		dma->CMAR = (uint)buf;
		dma->CNDTR = (uint)(n & 0xfff);
		dma->CCR |= DMA_CCR_EN;

		buf = (void*)((uint)buf + (uint)(n & 0xfff));
		n = n >> 16;
	}

	DBGDMAWR print("dmawr%d: write call done\n", c + 1);
}

ulong
dma_read(DmaChannel c, void *vbuf, ulong n)
{
	DBGDMARD print("dmard%d: read: %uld bytes\n", c + 1, n);

	Chanbuf *chanbuf = dma_chbufs[c];
	char *buf = vbuf;
	ulong m = 0;

	if(chanbuf == nil)
		panic("dma: channel %d has no chanbuf\n", c + 1);

	while(n) {
		DBGDMARD print("dmard%d: %uld bytes left\n", c + 1, n);

		for(; chanbuf->index < DMA_BUFSIZE && n > 0 &&
			chanbuf->buf[chanbuf->index] != CHANBUF_NULL;
			chanbuf->index++) {

			*buf = (char)(chanbuf->buf[chanbuf->index] & 0xff);
			chanbuf->buf[chanbuf->index] = CHANBUF_NULL;

			n--; m++; buf++;
		}

		if(chanbuf->index == DMA_BUFSIZE)
			chanbuf->index = 0;

		if(chanbuf->buf[chanbuf->index] & CHANBUF_NULL) {
			DBGDMARD print("dmard%d: read call done; %uld / %uld\n", c + 1, m, n);
			return m;
		}
	}

	DBGDMARD print("dmard%d: read call done; buffer full\n", c + 1);
	return n;
}

/*	DMA Channel 6	- USART2 RX	*/
void
dma_ch6init()
{
	DMA_Channel_TypeDef *dma = DMA1_Channel6;

	// dma configuration
	dma->CCR =  DMA_CCR_CIRC	|	// circular mode
				DMA_CCR_MINC;		// mem increment
	dma->CCR |= DMA_CCR_MSIZE_0;

	dma->CCR |= DMA_CCR_PL_0 | DMA_CCR_PL_1;

	// buffer init
	dma_chbufs[5] = malloc(sizeof(Chanbuf));
	dma_chbufs[5]->index = 0;
	for(int i = 0; i < DMA_BUFSIZE; i++)
		dma_chbufs[5]->buf[i] = CHANBUF_NULL;

	// specific addresses
	dma->CPAR = (uint)&USART2->DR;
	dma->CMAR = (uint)dma_chbufs[5]->buf;
	dma->CNDTR = DMA_BUFSIZE;
}

/*	DMA Channel 7	- USART2 TX	*/
void
dma_ch7init()
{
	DMA_Channel_TypeDef *dma = DMA1_Channel7;

	// dma configuration
	dma->CCR =  DMA_CCR_DIR		|	// mem->periph
				DMA_CCR_MINC;		// memory increment

	dma->CCR |= DMA_CCR_PL_0 | DMA_CCR_PL_1;

	// specific addresses
	dma->CPAR = (uint)&USART2->DR;
	dma->CNDTR = 0;
}

/*	DMA interrupt handlers section	*/

/*
void
dma_ch7handler()
{
	if(DMA1->ISR & DMA_ISR_TCIF7) {
		DMA1->IFCR |= DMA_IFCR_CGIF7;
		DMA1_Channel7->CCR &= ~DMA_CCR_EN;
	}
}
*/