Annotation of sys/arch/arm/xscale/pxa2x0_dmac.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: pxa2x0_dmac.c,v 1.3 2006/04/04 11:37:05 pascoe Exp $ */
2:
3: /*
4: * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: /*
20: * DMA Controller Handler for the Intel PXA2X0 processor.
21: */
22: #include <sys/cdefs.h>
23:
24: #include <sys/param.h>
25: #include <sys/systm.h>
26: #include <sys/malloc.h>
27: #include <sys/evcount.h>
28: #include <uvm/uvm_extern.h>
29:
30: #include <machine/bus.h>
31: #include <machine/intr.h>
32: #include <machine/lock.h>
33:
34: #include <arm/xscale/pxa2x0reg.h>
35: #include <arm/xscale/pxa2x0var.h>
36: #include <arm/xscale/pxa2x0_dmac.h>
37:
38: typedef void (*pxadmac_intrhandler)(void *);
39:
40: struct pxadmac_softc {
41: struct device sc_dev;
42: bus_space_tag_t sc_bust;
43: bus_space_handle_t sc_bush;
44: void *sc_ih;
45: int sc_nchan;
46: int sc_npri;
47: pxadmac_intrhandler sc_intrhandlers[DMAC_N_CHANNELS_PXA27X];
48: void *sc_intrargs[DMAC_N_CHANNELS_PXA27X];
49: };
50:
51: int pxadmac_intr(void *);
52:
53: /*
54: * DMAC autoconf glue
55: */
56: int pxadmac_match(struct device *, void *, void *);
57: void pxadmac_attach(struct device *, struct device *, void *);
58:
59: struct cfattach pxadmac_ca = {
60: sizeof(struct pxadmac_softc), pxadmac_match, pxadmac_attach
61: };
62:
63: struct cfdriver pxadmac_cd = {
64: NULL, "pxadmac", DV_DULL
65: };
66:
67: static struct pxadmac_softc *pxadmac_softc = NULL;
68:
69: int
70: pxadmac_match(struct device *parent, void *cf, void *aux)
71: {
72: struct pxaip_attach_args *pxa = aux;
73:
74: if (pxadmac_softc != NULL || pxa->pxa_addr != PXA2X0_DMAC_BASE)
75: return (0);
76:
77: return (1);
78: }
79:
80: void
81: pxadmac_attach(struct device *parent, struct device *self, void *args)
82: {
83: struct pxadmac_softc *sc = (struct pxadmac_softc *)self;
84: struct pxaip_attach_args *pxa = args;
85: bus_size_t bus_size;
86:
87: sc->sc_bust = pxa->pxa_iot;
88:
89: printf(": DMA Controller\n");
90:
91: if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X) {
92: sc->sc_nchan = DMAC_N_CHANNELS_PXA27X;
93: sc->sc_npri = DMAC_N_PRIORITIES_PXA27X;
94: bus_size = PXA27X_DMAC_SIZE;
95: } else {
96: sc->sc_nchan = DMAC_N_CHANNELS;
97: sc->sc_npri = DMAC_N_PRIORITIES;
98: bus_size = PXA2X0_DMAC_SIZE;
99: }
100:
101: if (bus_space_map(sc->sc_bust, pxa->pxa_addr, bus_size, 0,
102: &sc->sc_bush)) {
103: printf("%s: Can't map registers!\n", sc->sc_dev.dv_xname);
104: return;
105: }
106:
107: sc->sc_ih = pxa2x0_intr_establish(pxa->pxa_intr, IPL_BIO,
108: pxadmac_intr, sc, "pxadmac");
109: if (sc->sc_ih == NULL) {
110: printf(": unable to establish interrupt\n");
111: bus_space_unmap(sc->sc_bust, sc->sc_bush, bus_size);
112: return;
113: }
114:
115: pxadmac_softc = sc;
116: }
117:
118: /* Perform non-descriptor based DMA to a FIFO */
119: int
120: pxa2x0_dma_to_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
121: int burstsize, bus_addr_t src_addr, int length, void (*intr)(void *),
122: void *intrarg)
123: {
124: struct pxadmac_softc *sc = pxadmac_softc;
125: uint32_t cmd;
126:
127: if (periph < 0 || periph > 63 || periph == 23) {
128: printf("pxa2x0_dma_to_fifo: bogus peripheral %d", periph);
129: return EINVAL;
130: }
131:
132: if (chan < 0 || chan >= sc->sc_nchan) {
133: printf("pxa2x0_dma_to_fifo: bogus dma channel %d", chan);
134: return EINVAL;
135: }
136:
137: if (length < 0 || length > DCMD_LENGTH_MASK) {
138: printf("pxa2x0_dma_to_fifo: bogus length %d", length);
139: return EINVAL;
140: }
141:
142: cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCSRCADDR | DCMD_FLOWTRG
143: | DCMD_ENDIRQEN;
144:
145: switch (width) {
146: case 1:
147: cmd |= DCMD_WIDTH_1;
148: break;
149: case 4:
150: cmd |= DCMD_WIDTH_4;
151: break;
152: default:
153: printf("pxa2x0_dma_to_fifo: bogus width %d", width);
154: return EINVAL;
155: }
156:
157: switch (burstsize) {
158: case 8:
159: cmd |= DCMD_SIZE_8;
160: break;
161: case 16:
162: cmd |= DCMD_SIZE_16;
163: break;
164: case 32:
165: cmd |= DCMD_SIZE_32;
166: break;
167: default:
168: printf("pxa2x0_dma_to_fifo: bogus burstsize %d", burstsize);
169: return EINVAL;
170: }
171:
172: /* XXX: abort anything already in progress, hopefully nothing. */
173: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
174: DCSR_NODESCFETCH);
175:
176: /* Save handler for interrupt-on-completion. */
177: sc->sc_intrhandlers[chan] = intr;
178: sc->sc_intrargs[chan] = intrarg;
179:
180: /* Map peripheral to channel for flow control setup. */
181: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
182: chan | DRCMR_MAPVLD);
183:
184: /* Setup transfer addresses. */
185: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
186: DDADR_STOP);
187: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
188: src_addr);
189: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
190: fifo_addr);
191: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
192: cmd);
193:
194: /* Start the transfer. */
195: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
196: DCSR_RUN | DCSR_NODESCFETCH);
197:
198: return 0;
199: }
200:
201: /* Perform non-descriptor based DMA from a FIFO */
202: int
203: pxa2x0_dma_from_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
204: int burstsize, bus_addr_t trg_addr, int length, void (*intr)(void *),
205: void *intrarg)
206: {
207: struct pxadmac_softc *sc = pxadmac_softc;
208: uint32_t cmd;
209:
210: if (periph < 0 || periph > 63 || periph == 23) {
211: printf("pxa2x0_dma_from_fifo: bogus peripheral %d", periph);
212: return EINVAL;
213: }
214:
215: if (chan < 0 || chan >= sc->sc_nchan) {
216: printf("pxa2x0_dma_from_fifo: bogus dma channel %d", chan);
217: return EINVAL;
218: }
219:
220: if (length < 0 || length > DCMD_LENGTH_MASK) {
221: printf("pxa2x0_dma_from_fifo: bogus length %d", length);
222: return EINVAL;
223: }
224:
225: cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCTRGADDR | DCMD_FLOWSRC
226: | DCMD_ENDIRQEN;
227:
228: switch (width) {
229: case 1:
230: cmd |= DCMD_WIDTH_1;
231: break;
232: case 4:
233: cmd |= DCMD_WIDTH_4;
234: break;
235: default:
236: printf("pxa2x0_dma_from_fifo: bogus width %d", width);
237: return EINVAL;
238: }
239:
240: switch (burstsize) {
241: case 8:
242: cmd |= DCMD_SIZE_8;
243: break;
244: case 16:
245: cmd |= DCMD_SIZE_16;
246: break;
247: case 32:
248: cmd |= DCMD_SIZE_32;
249: break;
250: default:
251: printf("pxa2x0_dma_from_fifo: bogus burstsize %d", burstsize);
252: return EINVAL;
253: }
254:
255: /* XXX: abort anything already in progress, hopefully nothing. */
256: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
257: DCSR_NODESCFETCH);
258:
259: /* Save handler for interrupt-on-completion. */
260: sc->sc_intrhandlers[chan] = intr;
261: sc->sc_intrargs[chan] = intrarg;
262:
263: /* Map peripheral to channel for flow control setup. */
264: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
265: chan | DRCMR_MAPVLD);
266:
267: /* Setup transfer addresses. */
268: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
269: DDADR_STOP);
270: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
271: fifo_addr);
272: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
273: trg_addr);
274: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
275: cmd);
276:
277: /* Start the transfer. */
278: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
279: DCSR_RUN | DCSR_NODESCFETCH);
280:
281: return 0;
282: }
283:
284: int
285: pxadmac_intr(void *v)
286: {
287: struct pxadmac_softc *sc = v;
288: u_int32_t dint, dcsr;
289: int chan;
290:
291: /* Interrupt for us? */
292: dint = bus_space_read_4(sc->sc_bust, sc->sc_bush, DMAC_DINT);
293: if (!dint)
294: return 0;
295:
296: /* Process individual channels and run handlers. */
297: /* XXX: this does not respect priority order for channels. */
298: for (chan = 0; dint != 0 && chan < 32; chan++) {
299: /* Don't ack channels that weren't ready at call time. */
300: if ((dint & (1 << chan)) == 0)
301: continue;
302: dint &= ~(1 << chan);
303:
304: /* Acknowledge individual channel interrupt. */
305: dcsr = bus_space_read_4(sc->sc_bust, sc->sc_bush,
306: DMAC_DCSR(chan));
307: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
308: dcsr & 0x7C80021F);
309:
310: /* Call the registered handler. */
311: if (sc->sc_intrhandlers[chan])
312: sc->sc_intrhandlers[chan](sc->sc_intrargs[chan]);
313: }
314:
315: return 1;
316: }
CVSweb