~kris/9p

9hist

ref: 7cde4b94dce71805ab08320b1768b6e517936ef6 9hist/bitsy/sa1110dma.c -rw-r--r-- 2.7 KiB
7cde4b94 — David du Colombier Plan 9 from Bell Labs 2000-11-05 25 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

/*
 *	DMA helper routines
 */

enum {
	NDMA	=	6,			/* Number of DMA channels */
	DMAREGS	=	0xb0000000,	/* DMA registers, physical */
};

enum {
	/* Device Address Register, DDAR */
	RW		=	0,
	E		=	1,
	BS		=	2,
	DW		=	3,
	DS		=	4,	/* bits 4 - 7 */
	DA		=	8	/* bits 8 - 31 */
};

enum {
	/* Device Control & Status Register, DCSR */
	RUN		=	0,
	IE		=	1,
	ERROR	=	2,
	DONEA	=	3,
	STRTA	=	4,
	DONEB	=	5,
	STRTB	=	6,
	BIU		=	7
};

struct {
	Lock;
	Rendez	r[6];
	int		channels;
} dma;

struct dmaregs {
	ulong	ddar;
	ulong	dcsr_set;
	ulong	dcsr_clr;
	ulong	dcsr_rd;
	void*	dstrtA;
	ulong	dxcntA;
	void*	dstrtB;
	ulong	dxcntB;
} *dmaregs;

void
dmainit(void) {
	/* map the lcd regs into the kernel's virtual space */
	dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));;
}

int
dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, void *port) {
	int i;

	lock(&dma);
	for (i = 0; i < NDMA; i++) {
		if (dma.channels & (1 << i))
			continue;
		dma.channels |= 1 << i;
		unlock(&dma);
		dmaregs[i].ddar =
			(rd?1:0)<<RW |
			(bigendian?1:0)<<E |
			((burstsize==8)?1:0)<<BS |
			((datumsize==2)?1:0)<<DW |
			device<<DS |
			0x80000000 | ((ulong)port << 6);
		return i;
	}
	unlock(&dma);
	return -1;
}

void
dmafree(int i) {
	lock(&dma);
	dma.channels &= ~(1<<i);
	unlock(&dma);
}

static int
dmaready(void *dcsr) {
	return *(int*)dcsr & ((1<<DONEA)|(1<<DONEB));
}

ulong
dmastart(int chan, void *addr, int count) {
	ulong ab;

	while ((ab = dmaready(&dmaregs[chan].dcsr_rd)) == 0) {
		sleep(&dma.r[chan], dmaready, &dmaregs[chan].dcsr_rd);
	}
	cachewb();
	if (ab & (1<<DONEA)) {
		dmaregs[chan].dcsr_clr |= 1<<DONEA | 1<<STRTA;
		dmaregs[chan].dstrtA = addr;
		dmaregs[chan].dxcntA = count-1;
		dmaregs[chan].dcsr_set |= 1<<RUN | 1<<IE | 1<<STRTA;
		return 1<<DONEA;
	} else {
		dmaregs[chan].dcsr_clr |= 1<<DONEB | 1<<STRTB;
		dmaregs[chan].dstrtB = addr;
		dmaregs[chan].dxcntB = count-1;
		dmaregs[chan].dcsr_set |= 1<<RUN | 1<<IE | 1<<STRTB;
		return 1<<DONEB;
	}
}

ulong
dmadone(int chan, ulong op) {
	ulong dcsr;

	dcsr = dmaregs[chan].dcsr_rd;
	if (dcsr & 1<<ERROR)
		pprint("DMA error, chan %d, status 0x%lux\n", chan, dcsr);
	return dcsr & (op | 1<<ERROR);
}

void
dmawait(int chan, ulong op) {
	ulong dcsr;

	while (((dcsr = dmaregs[chan].dcsr_rd) & (op | 1<<ERROR)) == 0)
		sleep(&dma.r[chan], dmaready, &dmaregs[chan].dcsr_rd);
	if (dcsr & 1<<ERROR)
		pprint("DMA error, chan %d, status 0x%lux\n", chan, dcsr);
}

/*
 *  interrupt routine
 */
static void
dmaintr(Ureg*, void *x)
{
	int i;

	for (i = 0; i < NDMA; i++) {
		if (dmaregs[i].dcsr_rd & (1<<DONEA | 1<<DONEB | 1<<ERROR))
			wakeup(&dma.r[i]);
	}
}