summaryrefslogtreecommitdiff
path: root/drivers/dma/omap3_dma.c
blob: 3320b3d08d11fbd4382e9c735185ebc3576743e2 (plain)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* Copyright (C) 2011
 * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

/* This is a basic implementation of the SDMA/DMA4 controller of OMAP3
 * Tested on Silicon Revision major:0x4 minor:0x0
 */

#include <common.h>
#include <asm/arch/cpu.h>
#include <asm/arch/omap3.h>
#include <asm/arch/dma.h>
#include <asm/io.h>
#include <asm/errno.h>

static struct dma4 *dma4_cfg = (struct dma4 *)OMAP34XX_DMA4_BASE;
uint32_t dma_active; /* if a transfer is started the respective
	bit is set for the logical channel */

/* Check if we have the given channel
 * PARAMETERS:
 * chan: Channel number
 *
 * RETURN of non-zero means error */
static inline int check_channel(uint32_t chan)
{
	if (chan < CHAN_NR_MIN || chan > CHAN_NR_MAX)
		return -EINVAL;
	return 0;
}

static inline void reset_irq(uint32_t chan)
{
	/* reset IRQ reason */
	writel(0x1DFE, &dma4_cfg->chan[chan].csr);
	/* reset IRQ */
	writel((1 << chan), &dma4_cfg->irqstatus_l[0]);
	dma_active &= ~(1 << chan);
}

/* Set Source, Destination and Size of DMA transfer for the
 * specified channel.
 * PARAMETERS:
 * chan: channel to use
 * src: source of the transfer
 * dst: destination of the transfer
 * sze: Size of the transfer
 *
 * RETURN of non-zero means error */
int omap3_dma_conf_transfer(uint32_t chan, uint32_t *src, uint32_t *dst,
		uint32_t sze)
{
	if (check_channel(chan))
		return -EINVAL;
	/* CDSA0 */
	writel((uint32_t)src, &dma4_cfg->chan[chan].cssa);
	writel((uint32_t)dst, &dma4_cfg->chan[chan].cdsa);
	writel(sze, &dma4_cfg->chan[chan].cen);
return 0;
}

/* Start the DMA transfer */
int omap3_dma_start_transfer(uint32_t chan)
{
	uint32_t val;

	if (check_channel(chan))
		return -EINVAL;

	val = readl(&dma4_cfg->chan[chan].ccr);
	/* Test for channel already in use */
	if (val & CCR_ENABLE_ENABLE)
		return -EBUSY;

	writel((val | CCR_ENABLE_ENABLE), &dma4_cfg->chan[chan].ccr);
	dma_active |= (1 << chan);
	debug("started transfer...\n");
	return 0;
}

/* Busy-waiting for a DMA transfer
 * This has to be called before another transfer is started
 * PARAMETER
 * chan: Channel to wait for
 *
 * RETURN of non-zero means error*/
int omap3_dma_wait_for_transfer(uint32_t chan)
{
	uint32_t val;

	if (!(dma_active & (1 << chan))) {
		val = readl(&dma4_cfg->irqstatus_l[0]);
		if (!(val & chan)) {
			debug("dma: The channel you are trying to wait for "
				"was never activated - ERROR\n");
			return -1; /* channel was never active */
		}
	}

	/* all irqs on line 0 */
	while (!(readl(&dma4_cfg->irqstatus_l[0]) & (1 << chan)))
		asm("nop");

	val = readl(&dma4_cfg->chan[chan].csr);
	if ((val & CSR_TRANS_ERR) | (val & CSR_SUPERVISOR_ERR) |
			(val & CSR_MISALIGNED_ADRS_ERR)) {
		debug("err code: %X\n", val);
		debug("dma: transfer error detected\n");
		reset_irq(chan);
		return -1;
	}
	reset_irq(chan);
	return 0;
}

/* Get the revision of the DMA module
 * PARAMETER
 * minor: Address of minor revision to write
 * major: Address of major revision to write
 *
 * RETURN of non-zero means error
 */
int omap3_dma_get_revision(uint32_t *minor, uint32_t *major)
{
	uint32_t val;

	/* debug information */
	val = readl(&dma4_cfg->revision);
	*major = (val & 0x000000F0) >> 4;
	*minor = (val & 0x0000000F);
	debug("DMA Silicon revision (maj/min): 0x%X/0x%X\n", *major, *minor);
	return 0;
}

/* Initial config of omap dma
 */
void omap3_dma_init(void)
{
	dma_active = 0;
	/* All interrupts on channel 0 */
	writel(0xFFFFFFFF, &dma4_cfg->irqenable_l[0]);
}

/* set channel config to config
 *
 * RETURN of non-zero means error */
int omap3_dma_conf_chan(uint32_t chan, struct dma4_chan *config)
{
	if (check_channel(chan))
		return -EINVAL;

	dma4_cfg->chan[chan] = *config;
	return 0;
}

/* get channel config to config
 *
 * RETURN of non-zero means error */
int omap3_dma_get_conf_chan(uint32_t chan, struct dma4_chan *config)
{
	if (check_channel(chan))
		return -EINVAL;
	*config = dma4_cfg->chan[chan];
	return 0;
}