Project

General

Profile

Download (8.32 KB) Statistics
| Branch: | Tag: | Revision:
1

    
2
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
3
 *
4
 * All Rights Reserved
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19

    
20
#include <board.h>
21
#include <errno.h>
22
#include <irq/irq.h>
23
#include <dbgu/dbgu.h>
24
#include <ssc/ssc.h>
25
#include <pmc/pmc.h>
26
#include <utility/assert.h>
27
#include <utility/math.h>
28
#include <utility/trace.h>
29
#include <utility/led.h>
30

    
31
//#include <dmad/dmad.h>
32
#include <dma/dma.h>
33

    
34
#include <common.h>
35
#include <req_ctx.h>
36
#include <uart_cmd.h>
37

    
38
struct reg {
39
	unsigned int offset;
40
	const char *name;
41
};
42

    
43
void reg_dump(struct reg *regs, uint32_t *base)
44
{
45
	struct reg *r;
46
	for (r = regs; r->offset || r->name; r++) {
47
		uint32_t *addr = (uint32_t *) ((uint8_t *)base + r->offset);
48
		printf("%s\t%08x:\t%08x\n\r", r->name, addr, *addr);
49
	}
50
}
51

    
52

    
53
#define DMA_CTRLA	(AT91C_HDMA_SRC_WIDTH_WORD|AT91C_HDMA_DST_WIDTH_WORD|AT91C_HDMA_SCSIZE_1|AT91C_HDMA_DCSIZE_4)
54
#define DMA_CTRLB 	(AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM |	\
55
			 AT91C_HDMA_DST_ADDRESS_MODE_INCR |	\
56
			 AT91C_HDMA_SRC_DSCR_FETCH_DISABLE |	\
57
			 AT91C_HDMA_SRC_ADDRESS_MODE_FIXED |	\
58
			 AT91C_HDMA_FC_PER2MEM)
59

    
60
struct reg dma_regs[] = {
61
	{ 0,	"GCFG" },
62
	{ 4, 	"EN" },
63
	{ 8,	"SREQ" },
64
	{ 0xC,	"CREQ" },
65
	{ 0x10,	"LAST" },
66
	{ 0x20, "EBCIMR" },
67
	{ 0x24, "EBCISR" },
68
	{ 0x30, "CHSR" },
69
	{ 0,	NULL}
70
};
71

    
72
struct reg dma_ch_regs[] = {
73
	{ 0,	"SADDR" },
74
	{ 4,	"DADDR" },
75
	{ 8,	"DSCR" },
76
	{ 0xC,	"CTRLA" },
77
	{ 0x10, "CTRLB" },
78
	{ 0x14, "CFG" },
79
	{ 0,	NULL}
80
};
81

    
82
static void dma_dump_regs(void)
83
{
84
	reg_dump(dma_regs, (uint32_t *) AT91C_BASE_HDMA);
85
	reg_dump(dma_ch_regs, (uint8_t *)AT91C_BASE_HDMA_CH_0 + (BOARD_SSC_DMA_CHANNEL*0x28));
86
}
87

    
88
struct ssc_state {
89
	struct llist_head pending_rctx;
90
	int hdma_chain_len;
91
	int active;
92
	uint32_t total_xfers;
93
	uint32_t total_irqs;
94
	uint32_t total_ovrun;
95
};
96

    
97
struct ssc_state ssc_state;
98

    
99
#define INTENDED_HDMA_C_LEN	10
100

    
101
static void __refill_dma()
102
{
103
	int missing = INTENDED_HDMA_C_LEN - ssc_state.hdma_chain_len;
104
	int i;
105

    
106
	for (i = 0; i < missing; i++) {
107
		struct req_ctx *rctx;
108

    
109
		/* obtain an unused request context from pool */
110
		rctx = req_ctx_find_get(0, RCTX_STATE_FREE, RCTX_STATE_SSC_RX_PENDING);
111
		if (!rctx) {
112
			break;
113
		}
114

    
115
		/* populate DMA descriptor inside request context */
116
		rctx->dma_lli.sourceAddress = (unsigned int) &AT91C_BASE_SSC0->SSC_RHR;
117
		rctx->dma_lli.destAddress = rctx->data;
118
		rctx->dma_lli.controlA = DMA_CTRLA | (rctx->size/4);
119
		rctx->dma_lli.controlB = DMA_CTRLB;
120
		rctx->dma_lli.descriptor = 0; /* end of list */
121

    
122
		/* append to list and update end pointer */
123
		if (!llist_empty(&ssc_state.pending_rctx)) {
124
			struct req_ctx *prev_rctx = llist_entry(ssc_state.pending_rctx.prev,
125
								struct req_ctx, list);
126
			prev_rctx->dma_lli.descriptor = &rctx->dma_lli;
127
		}
128
		req_ctx_enqueue(&ssc_state.pending_rctx, rctx);
129
		ssc_state.hdma_chain_len++;
130
	}
131
	/*
132
	if (ssc_state.hdma_chain_len <= 1)
133
		TRACE_ERROR("Unable to get rctx for SSC DMA refill\n\r");
134
	*/
135
}
136

    
137
int ssc_dma_start(void)
138
{
139
	struct req_ctx *rctx;
140

    
141
	__refill_dma();
142

    
143
	if (ssc_state.active) {
144
		//TRACE_WARNING("Cannot start SSC DMA, active == 1\n\r");
145
		return -EBUSY;
146
	}
147

    
148
	if (llist_empty(&ssc_state.pending_rctx)) {
149
		//TRACE_WARNING("Cannot start SSC DMA, no rctx pending\n\r");
150
		return -ENOMEM;
151
	}
152

    
153
	rctx = llist_entry(ssc_state.pending_rctx.next, struct req_ctx, list);
154

    
155
	/* clear any pending interrupts */
156
	DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
157
	DMA_GetStatus();
158

    
159
	DMA_SetDescriptorAddr(BOARD_SSC_DMA_CHANNEL, &rctx->dma_lli);
160
	DMA_SetSourceAddr(BOARD_SSC_DMA_CHANNEL, &AT91C_BASE_SSC0->SSC_RHR);
161
	DMA_SetSourceBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
162
				(AT91C_HDMA_SRC_ADDRESS_MODE_FIXED >> 24));
163
	DMA_SetDestBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
164
				(AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28));
165
	DMA_SetFlowControl(BOARD_SSC_DMA_CHANNEL, AT91C_HDMA_FC_PER2MEM >> 21);
166
	DMA_SetConfiguration(BOARD_SSC_DMA_CHANNEL,
167
				BOARD_SSC_DMA_HW_SRC_REQ_ID | BOARD_SSC_DMA_HW_DEST_REQ_ID
168
				| AT91C_HDMA_SRC_H2SEL_HW \
169
				| AT91C_HDMA_DST_H2SEL_SW \
170
				| AT91C_HDMA_SOD_DISABLE \
171
				| AT91C_HDMA_FIFOCFG_LARGESTBURST);
172

    
173
	ssc_state.active = 1;
174
	DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL);
175
	LED_Set(0);
176
	TRACE_INFO("Started SSC DMA\n\r");
177

    
178
	SSC_EnableReceiver(AT91C_BASE_SSC0);
179

    
180
	return 0;
181
}
182

    
183
int ssc_dma_stop(void)
184
{
185
	SSC_DisableReceiver(AT91C_BASE_SSC0);
186
	ssc_state.active = 0;
187

    
188
	/* clear any pending interrupts */
189
	DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
190
	DMA_GetStatus();
191

    
192
	return 0;
193
}
194

    
195

    
196
#define BTC(N)	(1 << N)
197
#define CBTC(N)	(1 << (8+N))
198
#define	ERR(N)	(1 << (16+N))
199

    
200

    
201
/* for some strange reason this cannot be static! */
202
void HDMA_IrqHandler(void)
203
{
204
	unsigned int status = DMA_GetStatus();
205
	struct req_ctx *rctx, *rctx2;
206

    
207
	ssc_state.total_irqs++;
208

    
209
	if (status & BTC(BOARD_SSC_DMA_CHANNEL)) {
210
		llist_for_each_entry_safe(rctx, rctx2, &ssc_state.pending_rctx, list) {
211
			if (!(rctx->dma_lli.controlA & AT91C_HDMA_DONE))
212
				continue;
213

    
214
			/* a single buffer has been completed */
215
			ssc_state.total_xfers++;
216
			llist_del(&rctx->list);
217
			ssc_state.hdma_chain_len--;
218
			rctx->tot_len = rctx->size;
219
#if 1
220
			usb_submit_req_ctx(rctx);
221
#else
222
			req_ctx_set_state(rctx, RCTX_STATE_FREE);
223
#endif
224

    
225
			__refill_dma();
226

    
227
		}
228
	}
229
	if (status & CBTC(BOARD_SSC_DMA_CHANNEL)) {
230
		/* the end of the list was reached */
231
		LED_Clear(0);
232
		SSC_DisableReceiver(AT91C_BASE_SSC0);
233
		ssc_state.active = 0;
234
		TRACE_WARNING("SSC DMA buffer end reached, disabling after %u/%u\n\r",
235
				ssc_state.total_irqs, ssc_state.total_xfers);
236
	}
237
}
238

    
239
void ssc_stats(void)
240
{
241
	printf("SSC num_irq=%u, num_xfers=%u, num_ovrun=%u\n\r",
242
		ssc_state.total_irqs, ssc_state.total_xfers,
243
		ssc_state.total_ovrun);
244
}
245

    
246
void SSC0_IrqHandler(void)
247
{
248
	if (AT91C_BASE_SSC0->SSC_SR & AT91C_SSC_OVRUN)
249
		ssc_state.total_ovrun++;
250
}
251

    
252
int ssc_active(void)
253
{
254
	return ssc_state.active;
255
}
256

    
257

    
258
static int cmd_ssc_start(struct cmd_state *cs, enum cmd_op op,
259
			 const char *cmd, int argc, char **argv)
260
{
261
	ssc_init();
262
	ssc_dma_start();
263
	return 0;
264
}
265
static int cmd_ssc_stop(struct cmd_state *cs, enum cmd_op op,
266
			 const char *cmd, int argc, char **argv)
267
{
268
	ssc_dma_stop();
269
	return 0;
270
}
271
static int cmd_ssc_stats(struct cmd_state *cs, enum cmd_op op,
272
			 const char *cmd, int argc, char **argv)
273
{
274
	ssc_stats();
275
	return 0;
276
}
277
static int cmd_ssc_dump(struct cmd_state *cs, enum cmd_op op,
278
			const char *cmd, int argc, char **argv)
279
{
280
	struct req_ctx *rctx, *rctx2;
281

    
282
	dma_dump_regs();
283
	req_ctx_dump();
284

    
285
	printf("ssc pending:");
286
	llist_for_each_entry_safe(rctx, rctx2, &ssc_state.pending_rctx, list)
287
		printf(" %02d", req_ctx_num(rctx));
288
	printf("\n\r");
289
	fastsource_dump();
290
}
291

    
292
static struct cmd cmds[] = {
293
	{ "ssc.start", CMD_OP_EXEC, cmd_ssc_start,
294
	  "Start the SSC Receiver" },
295
	{ "ssc.stop", CMD_OP_EXEC, cmd_ssc_stop,
296
	  "Start the SSC Receiver" },
297
	{ "ssc.stats", CMD_OP_EXEC, cmd_ssc_stats,
298
	  "Statistics about the SSC" },
299
	{ "ssc.dump", CMD_OP_EXEC, cmd_ssc_dump,
300
	  "Dump SSC DMA registers" },
301
};
302

    
303
int ssc_init(void)
304
{
305
	memset(&ssc_state, 0, sizeof(ssc_state));
306
	INIT_LLIST_HEAD(&ssc_state.pending_rctx);
307

    
308
	SSC_DisableReceiver(AT91C_BASE_SSC0);
309
	SSC_Configure(AT91C_BASE_SSC0, AT91C_ID_SSC0, 0, BOARD_MCK);
310
	SSC_ConfigureReceiver(AT91C_BASE_SSC0, AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
311
					 AT91C_SSC_CKG_NONE | AT91C_SSC_START_RISE_RF |
312
					 AT91C_SSC_CKI,
313
					 AT91C_SSC_MSBF | (32-1) );
314

    
315
	SSC_DisableInterrupts(AT91C_BASE_SSC0, 0xffffffff);
316
	SSC_EnableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_OVRUN);
317

    
318
	/* Enable Overrun interrupts */
319
	IRQ_ConfigureIT(AT91C_ID_SSC0, 0, SSC0_IrqHandler);
320
	IRQ_EnableIT(AT91C_ID_SSC0);
321

    
322
	/* Enable DMA controller and register interrupt handler */
323
	PMC_EnablePeripheral(AT91C_ID_HDMA);
324
	DMA_Enable();
325
	IRQ_ConfigureIT(AT91C_ID_HDMA, 0, HDMA_IrqHandler);
326
	IRQ_EnableIT(AT91C_ID_HDMA);
327
	DMA_EnableIt(BTC(BOARD_SSC_DMA_CHANNEL) | CBTC(BOARD_SSC_DMA_CHANNEL) |
328
		     ERR(BOARD_SSC_DMA_CHANNEL));
329

    
330
	TRACE_INFO("SSC initialized\n\r");
331
	LED_Clear(0);
332

    
333
	uart_cmds_register(cmds, ARRAY_SIZE(cmds));
334

    
335
	return 0;
336
}
(5-5/11)
Add picture from clipboard (Maximum size: 48.8 MB)