xref: /netbsd-src/sys/arch/arm/xscale/iopaau.c (revision 5f9a3f32455c931cf2bec06218a57bc85543afbd)
1*5f9a3f32Smaxv /*	$NetBSD: iopaau.c,v 1.18 2019/03/17 06:36:22 maxv Exp $	*/
2036da55eSthorpej 
3036da55eSthorpej /*
4036da55eSthorpej  * Copyright (c) 2002 Wasabi Systems, Inc.
5036da55eSthorpej  * All rights reserved.
6036da55eSthorpej  *
7036da55eSthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8036da55eSthorpej  *
9036da55eSthorpej  * Redistribution and use in source and binary forms, with or without
10036da55eSthorpej  * modification, are permitted provided that the following conditions
11036da55eSthorpej  * are met:
12036da55eSthorpej  * 1. Redistributions of source code must retain the above copyright
13036da55eSthorpej  *    notice, this list of conditions and the following disclaimer.
14036da55eSthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15036da55eSthorpej  *    notice, this list of conditions and the following disclaimer in the
16036da55eSthorpej  *    documentation and/or other materials provided with the distribution.
17036da55eSthorpej  * 3. All advertising materials mentioning features or use of this software
18036da55eSthorpej  *    must display the following acknowledgement:
19036da55eSthorpej  *	This product includes software developed for the NetBSD Project by
20036da55eSthorpej  *	Wasabi Systems, Inc.
21036da55eSthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22036da55eSthorpej  *    or promote products derived from this software without specific prior
23036da55eSthorpej  *    written permission.
24036da55eSthorpej  *
25036da55eSthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26036da55eSthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27036da55eSthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28036da55eSthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29036da55eSthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30036da55eSthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31036da55eSthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32036da55eSthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33036da55eSthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34036da55eSthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35036da55eSthorpej  * POSSIBILITY OF SUCH DAMAGE.
36036da55eSthorpej  */
37036da55eSthorpej 
38036da55eSthorpej /*
39036da55eSthorpej  * Common code for XScale-based I/O Processor Application Accelerator
40036da55eSthorpej  * Unit support.
41036da55eSthorpej  *
42036da55eSthorpej  * The AAU provides a back-end for the dmover(9) facility.
43036da55eSthorpej  */
44036da55eSthorpej 
45036da55eSthorpej #include <sys/cdefs.h>
46*5f9a3f32Smaxv __KERNEL_RCSID(0, "$NetBSD: iopaau.c,v 1.18 2019/03/17 06:36:22 maxv Exp $");
47036da55eSthorpej 
48036da55eSthorpej #include <sys/param.h>
49036da55eSthorpej #include <sys/pool.h>
50036da55eSthorpej #include <sys/systm.h>
51036da55eSthorpej #include <sys/device.h>
52036da55eSthorpej #include <sys/uio.h>
53c29520cbSad #include <sys/bus.h>
54036da55eSthorpej 
55036da55eSthorpej #include <uvm/uvm.h>
56036da55eSthorpej 
57036da55eSthorpej #include <arm/xscale/iopaaureg.h>
58036da55eSthorpej #include <arm/xscale/iopaauvar.h>
59036da55eSthorpej 
60036da55eSthorpej #ifdef AAU_DEBUG
61036da55eSthorpej #define	DPRINTF(x)	printf x
62036da55eSthorpej #else
63036da55eSthorpej #define	DPRINTF(x)	/* nothing */
64036da55eSthorpej #endif
65036da55eSthorpej 
66d18c6ca4Sad pool_cache_t iopaau_desc_4_cache;
67d18c6ca4Sad pool_cache_t iopaau_desc_8_cache;
68036da55eSthorpej 
69036da55eSthorpej /*
70036da55eSthorpej  * iopaau_desc_ctor:
71036da55eSthorpej  *
72036da55eSthorpej  *	Constructor for all types of descriptors.
73036da55eSthorpej  */
74036da55eSthorpej static int
iopaau_desc_ctor(void * arg,void * object,int flags)75036da55eSthorpej iopaau_desc_ctor(void *arg, void *object, int flags)
76036da55eSthorpej {
77036da55eSthorpej 	struct aau_desc_4 *d = object;
78036da55eSthorpej 
79036da55eSthorpej 	/*
80036da55eSthorpej 	 * Cache the physical address of the hardware portion of
81036da55eSthorpej 	 * the descriptor in the software portion of the descriptor
82036da55eSthorpej 	 * for quick reference later.
83036da55eSthorpej 	 */
8423d2066aSthorpej 	d->d_pa = vtophys((vaddr_t)d) + SYNC_DESC_4_OFFSET;
85036da55eSthorpej 	KASSERT((d->d_pa & 31) == 0);
86036da55eSthorpej 	return (0);
87036da55eSthorpej }
88036da55eSthorpej 
89036da55eSthorpej /*
90a39c3378Sthorpej  * iopaau_desc_free:
91036da55eSthorpej  *
92a39c3378Sthorpej  *	Free a chain of AAU descriptors.
93036da55eSthorpej  */
94036da55eSthorpej void
iopaau_desc_free(struct pool_cache * dc,void * firstdesc)95a39c3378Sthorpej iopaau_desc_free(struct pool_cache *dc, void *firstdesc)
96036da55eSthorpej {
97036da55eSthorpej 	struct aau_desc_4 *d, *next;
98036da55eSthorpej 
99036da55eSthorpej 	for (d = firstdesc; d != NULL; d = next) {
100036da55eSthorpej 		next = d->d_next;
101a39c3378Sthorpej 		pool_cache_put(dc, d);
102036da55eSthorpej 	}
103036da55eSthorpej }
104036da55eSthorpej 
105036da55eSthorpej /*
106036da55eSthorpej  * iopaau_start:
107036da55eSthorpej  *
108036da55eSthorpej  *	Start an AAU request.  Must be called at splbio().
109036da55eSthorpej  */
110036da55eSthorpej static void
iopaau_start(struct iopaau_softc * sc)111036da55eSthorpej iopaau_start(struct iopaau_softc *sc)
112036da55eSthorpej {
113036da55eSthorpej 	struct dmover_backend *dmb = &sc->sc_dmb;
114036da55eSthorpej 	struct dmover_request *dreq;
115036da55eSthorpej 	struct iopaau_function *af;
116036da55eSthorpej 	int error;
117036da55eSthorpej 
118036da55eSthorpej 	for (;;) {
119036da55eSthorpej 
120036da55eSthorpej 		KASSERT(sc->sc_running == NULL);
121036da55eSthorpej 
122036da55eSthorpej 		dreq = TAILQ_FIRST(&dmb->dmb_pendreqs);
123036da55eSthorpej 		if (dreq == NULL)
124036da55eSthorpej 			return;
125036da55eSthorpej 
126036da55eSthorpej 		dmover_backend_remque(dmb, dreq);
127036da55eSthorpej 		dreq->dreq_flags |= DMOVER_REQ_RUNNING;
128036da55eSthorpej 
129036da55eSthorpej 		sc->sc_running = dreq;
130036da55eSthorpej 
131036da55eSthorpej 		/* XXXUNLOCK */
132036da55eSthorpej 
133036da55eSthorpej 		af = dreq->dreq_assignment->das_algdesc->dad_data;
134036da55eSthorpej 		error = (*af->af_setup)(sc, dreq);
135036da55eSthorpej 
136036da55eSthorpej 		/* XXXLOCK */
137036da55eSthorpej 
138036da55eSthorpej 		if (error) {
139036da55eSthorpej 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
140036da55eSthorpej 			dreq->dreq_error = error;
141036da55eSthorpej 			sc->sc_running = NULL;
142036da55eSthorpej 			/* XXXUNLOCK */
143036da55eSthorpej 			dmover_done(dreq);
144036da55eSthorpej 			/* XXXLOCK */
145036da55eSthorpej 			continue;
146036da55eSthorpej 		}
147036da55eSthorpej 
148036da55eSthorpej #ifdef DIAGNOSTIC
149036da55eSthorpej 		if (bus_space_read_4(sc->sc_st, sc->sc_sh, AAU_ASR) &
150036da55eSthorpej 		    AAU_ASR_AAF)
151036da55eSthorpej 			panic("iopaau_start: AAU already active");
152036da55eSthorpej #endif
153036da55eSthorpej 
1547490f933Smatt 		DPRINTF(("%s: starting dreq %p\n", device_xname(sc->sc_dev),
155036da55eSthorpej 		    dreq));
156036da55eSthorpej 
157036da55eSthorpej 		bus_space_write_4(sc->sc_st, sc->sc_sh, AAU_ANDAR,
158036da55eSthorpej 		    sc->sc_firstdesc_pa);
159036da55eSthorpej 		bus_space_write_4(sc->sc_st, sc->sc_sh, AAU_ACR,
160036da55eSthorpej 		    AAU_ACR_AAE);
161036da55eSthorpej 
162036da55eSthorpej 		break;
163036da55eSthorpej 	}
164036da55eSthorpej }
165036da55eSthorpej 
166036da55eSthorpej /*
167036da55eSthorpej  * iopaau_finish:
168036da55eSthorpej  *
169036da55eSthorpej  *	Finish the current operation.  AAU must be stopped.
170036da55eSthorpej  */
171036da55eSthorpej static void
iopaau_finish(struct iopaau_softc * sc)172036da55eSthorpej iopaau_finish(struct iopaau_softc *sc)
173036da55eSthorpej {
174036da55eSthorpej 	struct dmover_request *dreq = sc->sc_running;
175036da55eSthorpej 	struct iopaau_function *af =
176036da55eSthorpej 	    dreq->dreq_assignment->das_algdesc->dad_data;
177036da55eSthorpej 	void *firstdesc = sc->sc_firstdesc;
178036da55eSthorpej 	int i, ninputs = dreq->dreq_assignment->das_algdesc->dad_ninputs;
179036da55eSthorpej 
180036da55eSthorpej 	sc->sc_running = NULL;
181036da55eSthorpej 
182036da55eSthorpej 	/* If the function has inputs, unmap them. */
183036da55eSthorpej 	for (i = 0; i < ninputs; i++) {
184036da55eSthorpej 		bus_dmamap_sync(sc->sc_dmat, sc->sc_map_in[i], 0,
185c070073dSthorpej 		    sc->sc_map_in[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE);
186036da55eSthorpej 		bus_dmamap_unload(sc->sc_dmat, sc->sc_map_in[i]);
187036da55eSthorpej 	}
188036da55eSthorpej 
189036da55eSthorpej 	/* Unload the output buffer DMA map. */
190036da55eSthorpej 	bus_dmamap_sync(sc->sc_dmat, sc->sc_map_out, 0,
191c070073dSthorpej 	    sc->sc_map_out->dm_mapsize, BUS_DMASYNC_POSTREAD);
192036da55eSthorpej 	bus_dmamap_unload(sc->sc_dmat, sc->sc_map_out);
193036da55eSthorpej 
194036da55eSthorpej 	/* Get the next transfer started. */
195036da55eSthorpej 	iopaau_start(sc);
196036da55eSthorpej 
197036da55eSthorpej 	/* Now free descriptors for last transfer. */
198a39c3378Sthorpej 	iopaau_desc_free(af->af_desc_cache, firstdesc);
199036da55eSthorpej 
200036da55eSthorpej 	dmover_done(dreq);
201036da55eSthorpej }
202036da55eSthorpej 
203036da55eSthorpej /*
204036da55eSthorpej  * iopaau_process:
205036da55eSthorpej  *
206036da55eSthorpej  *	Dmover back-end entry point.
207036da55eSthorpej  */
208036da55eSthorpej void
iopaau_process(struct dmover_backend * dmb)209036da55eSthorpej iopaau_process(struct dmover_backend *dmb)
210036da55eSthorpej {
211036da55eSthorpej 	struct iopaau_softc *sc = dmb->dmb_cookie;
212036da55eSthorpej 	int s;
213036da55eSthorpej 
214036da55eSthorpej 	s = splbio();
215036da55eSthorpej 	/* XXXLOCK */
216036da55eSthorpej 
217036da55eSthorpej 	if (sc->sc_running == NULL)
218036da55eSthorpej 		iopaau_start(sc);
219036da55eSthorpej 
220036da55eSthorpej 	/* XXXUNLOCK */
221036da55eSthorpej 	splx(s);
222036da55eSthorpej }
223036da55eSthorpej 
224036da55eSthorpej /*
22558983a92Sthorpej  * iopaau_func_fill_immed_setup:
226036da55eSthorpej  *
22758983a92Sthorpej  *	Common code shared by the zero and fillN setup routines.
228036da55eSthorpej  */
22958983a92Sthorpej static int
iopaau_func_fill_immed_setup(struct iopaau_softc * sc,struct dmover_request * dreq,uint32_t immed)23058983a92Sthorpej iopaau_func_fill_immed_setup(struct iopaau_softc *sc,
23158983a92Sthorpej     struct dmover_request *dreq, uint32_t immed)
232036da55eSthorpej {
233a39c3378Sthorpej 	struct iopaau_function *af =
234a39c3378Sthorpej 	    dreq->dreq_assignment->das_algdesc->dad_data;
235a39c3378Sthorpej 	struct pool_cache *dc = af->af_desc_cache;
236036da55eSthorpej 	bus_dmamap_t dmamap = sc->sc_map_out;
237036da55eSthorpej 	uint32_t *prevpa;
238036da55eSthorpej 	struct aau_desc_4 **prevp, *cur;
239036da55eSthorpej 	int error, seg;
240036da55eSthorpej 
241036da55eSthorpej 	switch (dreq->dreq_outbuf_type) {
242036da55eSthorpej 	case DMOVER_BUF_LINEAR:
243036da55eSthorpej 		error = bus_dmamap_load(sc->sc_dmat, dmamap,
244036da55eSthorpej 		    dreq->dreq_outbuf.dmbuf_linear.l_addr,
245036da55eSthorpej 		    dreq->dreq_outbuf.dmbuf_linear.l_len, NULL,
246c070073dSthorpej 		    BUS_DMA_NOWAIT|BUS_DMA_READ|BUS_DMA_STREAMING);
247036da55eSthorpej 		break;
248036da55eSthorpej 
249036da55eSthorpej 	case DMOVER_BUF_UIO:
250036da55eSthorpej 	    {
251036da55eSthorpej 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
252036da55eSthorpej 
253036da55eSthorpej 		if (uio->uio_rw != UIO_READ)
254036da55eSthorpej 			return (EINVAL);
255036da55eSthorpej 
256036da55eSthorpej 		error = bus_dmamap_load_uio(sc->sc_dmat, dmamap,
257c070073dSthorpej 		    uio, BUS_DMA_NOWAIT|BUS_DMA_READ|BUS_DMA_STREAMING);
258036da55eSthorpej 		break;
259036da55eSthorpej 	    }
26090044065Sthorpej 
26190044065Sthorpej 	default:
26290044065Sthorpej 		error = EINVAL;
263036da55eSthorpej 	}
264036da55eSthorpej 
265036da55eSthorpej 	if (__predict_false(error != 0))
266036da55eSthorpej 		return (error);
267036da55eSthorpej 
268036da55eSthorpej 	bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
269c070073dSthorpej 	    BUS_DMASYNC_PREREAD);
270036da55eSthorpej 
271036da55eSthorpej 	prevp = (struct aau_desc_4 **) &sc->sc_firstdesc;
272036da55eSthorpej 	prevpa = &sc->sc_firstdesc_pa;
273036da55eSthorpej 
274b9e31106Smatt 	cur = NULL;	/* XXX: gcc */
275036da55eSthorpej 	for (seg = 0; seg < dmamap->dm_nsegs; seg++) {
276a39c3378Sthorpej 		cur = pool_cache_get(dc, PR_NOWAIT);
277036da55eSthorpej 		if (cur == NULL) {
278036da55eSthorpej 			*prevp = NULL;
279036da55eSthorpej 			error = ENOMEM;
280036da55eSthorpej 			goto bad;
281036da55eSthorpej 		}
282036da55eSthorpej 
283036da55eSthorpej 		*prevp = cur;
284036da55eSthorpej 		*prevpa = cur->d_pa;
285036da55eSthorpej 
286036da55eSthorpej 		prevp = &cur->d_next;
287036da55eSthorpej 		prevpa = &cur->d_nda;
288036da55eSthorpej 
289036da55eSthorpej 		/*
290036da55eSthorpej 		 * We don't actually enforce the page alignment
291036da55eSthorpej 		 * constraint, here, because there is only one
292036da55eSthorpej 		 * data stream to worry about.
293036da55eSthorpej 		 */
294036da55eSthorpej 
295c070073dSthorpej 		cur->d_sar[0] = immed;
296036da55eSthorpej 		cur->d_dar = dmamap->dm_segs[seg].ds_addr;
297036da55eSthorpej 		cur->d_bc = dmamap->dm_segs[seg].ds_len;
298036da55eSthorpej 		cur->d_dc = AAU_DC_B1_CC(AAU_DC_CC_FILL) | AAU_DC_DWE;
2993b50c171Sthorpej 		SYNC_DESC(cur, sizeof(struct aau_desc_4));
300036da55eSthorpej 	}
301036da55eSthorpej 
302036da55eSthorpej 	*prevp = NULL;
303036da55eSthorpej 	*prevpa = 0;
304036da55eSthorpej 
305036da55eSthorpej 	cur->d_dc |= AAU_DC_IE;
3063b50c171Sthorpej 	SYNC_DESC(cur, sizeof(struct aau_desc_4));
307036da55eSthorpej 
308036da55eSthorpej 	sc->sc_lastdesc = cur;
309036da55eSthorpej 
310036da55eSthorpej 	return (0);
311036da55eSthorpej 
312036da55eSthorpej  bad:
313a39c3378Sthorpej 	iopaau_desc_free(dc, sc->sc_firstdesc);
314036da55eSthorpej 	bus_dmamap_unload(sc->sc_dmat, sc->sc_map_out);
315036da55eSthorpej 	sc->sc_firstdesc = NULL;
316036da55eSthorpej 
317036da55eSthorpej 	return (error);
318036da55eSthorpej }
319036da55eSthorpej 
320036da55eSthorpej /*
32158983a92Sthorpej  * iopaau_func_zero_setup:
32258983a92Sthorpej  *
32358983a92Sthorpej  *	Setup routine for the "zero" function.
32458983a92Sthorpej  */
32558983a92Sthorpej int
iopaau_func_zero_setup(struct iopaau_softc * sc,struct dmover_request * dreq)32658983a92Sthorpej iopaau_func_zero_setup(struct iopaau_softc *sc, struct dmover_request *dreq)
32758983a92Sthorpej {
32858983a92Sthorpej 
32958983a92Sthorpej 	return (iopaau_func_fill_immed_setup(sc, dreq, 0));
33058983a92Sthorpej }
33158983a92Sthorpej 
33258983a92Sthorpej /*
33358983a92Sthorpej  * iopaau_func_fill8_setup:
33458983a92Sthorpej  *
33558983a92Sthorpej  *	Setup routine for the "fill8" function.
33658983a92Sthorpej  */
33758983a92Sthorpej int
iopaau_func_fill8_setup(struct iopaau_softc * sc,struct dmover_request * dreq)33858983a92Sthorpej iopaau_func_fill8_setup(struct iopaau_softc *sc, struct dmover_request *dreq)
33958983a92Sthorpej {
34058983a92Sthorpej 
34158983a92Sthorpej 	return (iopaau_func_fill_immed_setup(sc, dreq,
34258983a92Sthorpej 	    dreq->dreq_immediate[0] |
34358983a92Sthorpej 	    (dreq->dreq_immediate[0] << 8) |
34458983a92Sthorpej 	    (dreq->dreq_immediate[0] << 16) |
34558983a92Sthorpej 	    (dreq->dreq_immediate[0] << 24)));
34658983a92Sthorpej }
34758983a92Sthorpej 
34858983a92Sthorpej /*
349c070073dSthorpej  * Descriptor command words for varying numbers of inputs.  For 1 input,
350c070073dSthorpej  * this does a copy.  For multiple inputs, we're doing an XOR.  In this
351c070073dSthorpej  * case, the first block is a "direct fill" to load the store queue, and
352c070073dSthorpej  * the remaining blocks are XOR'd to the store queue.
353c070073dSthorpej  */
354c070073dSthorpej static const uint32_t iopaau_dc_inputs[] = {
355c070073dSthorpej 	0,						/* 0 */
356c070073dSthorpej 
357c070073dSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL),		/* 1 */
358c070073dSthorpej 
359c070073dSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|		/* 2 */
360c070073dSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR),
361c070073dSthorpej 
362c070073dSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|		/* 3 */
363c070073dSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
364c070073dSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR),
365c070073dSthorpej 
366c070073dSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|		/* 4 */
367c070073dSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
368c070073dSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR)|
369c070073dSthorpej 	AAU_DC_B4_CC(AAU_DC_CC_XOR),
3700aa15bdfSthorpej 
3710aa15bdfSthorpej 	AAU_DC_SBCI_5_8|				/* 5 */
3720aa15bdfSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|
3730aa15bdfSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
3740aa15bdfSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR)|
3750aa15bdfSthorpej 	AAU_DC_B4_CC(AAU_DC_CC_XOR)|
3760aa15bdfSthorpej 	AAU_DC_B5_CC(AAU_DC_CC_XOR),
3770aa15bdfSthorpej 
3780aa15bdfSthorpej 	AAU_DC_SBCI_5_8|				/* 6 */
3790aa15bdfSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|
3800aa15bdfSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
3810aa15bdfSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR)|
3820aa15bdfSthorpej 	AAU_DC_B4_CC(AAU_DC_CC_XOR)|
3830aa15bdfSthorpej 	AAU_DC_B5_CC(AAU_DC_CC_XOR)|
3840aa15bdfSthorpej 	AAU_DC_B6_CC(AAU_DC_CC_XOR),
3850aa15bdfSthorpej 
3860aa15bdfSthorpej 	AAU_DC_SBCI_5_8|				/* 7 */
3870aa15bdfSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|
3880aa15bdfSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
3890aa15bdfSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR)|
3900aa15bdfSthorpej 	AAU_DC_B4_CC(AAU_DC_CC_XOR)|
3910aa15bdfSthorpej 	AAU_DC_B5_CC(AAU_DC_CC_XOR)|
3920aa15bdfSthorpej 	AAU_DC_B6_CC(AAU_DC_CC_XOR)|
3930aa15bdfSthorpej 	AAU_DC_B7_CC(AAU_DC_CC_XOR),
3940aa15bdfSthorpej 
3950aa15bdfSthorpej 	AAU_DC_SBCI_5_8|				/* 8 */
3960aa15bdfSthorpej 	AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL)|
3970aa15bdfSthorpej 	AAU_DC_B2_CC(AAU_DC_CC_XOR)|
3980aa15bdfSthorpej 	AAU_DC_B3_CC(AAU_DC_CC_XOR)|
3990aa15bdfSthorpej 	AAU_DC_B4_CC(AAU_DC_CC_XOR)|
4000aa15bdfSthorpej 	AAU_DC_B5_CC(AAU_DC_CC_XOR)|
4010aa15bdfSthorpej 	AAU_DC_B6_CC(AAU_DC_CC_XOR)|
4020aa15bdfSthorpej 	AAU_DC_B7_CC(AAU_DC_CC_XOR)|
4030aa15bdfSthorpej 	AAU_DC_B8_CC(AAU_DC_CC_XOR),
404c070073dSthorpej };
405c070073dSthorpej 
406c070073dSthorpej /*
4070aa15bdfSthorpej  * iopaau_func_xor_setup:
408036da55eSthorpej  *
4090aa15bdfSthorpej  *	Setup routine for the "copy", "xor2".."xor8" functions.
410036da55eSthorpej  */
411036da55eSthorpej int
iopaau_func_xor_setup(struct iopaau_softc * sc,struct dmover_request * dreq)4120aa15bdfSthorpej iopaau_func_xor_setup(struct iopaau_softc *sc, struct dmover_request *dreq)
413036da55eSthorpej {
414a39c3378Sthorpej 	struct iopaau_function *af =
415a39c3378Sthorpej 	    dreq->dreq_assignment->das_algdesc->dad_data;
416a39c3378Sthorpej 	struct pool_cache *dc = af->af_desc_cache;
417036da55eSthorpej 	bus_dmamap_t dmamap = sc->sc_map_out;
418c070073dSthorpej 	bus_dmamap_t *inmap = sc->sc_map_in;
419036da55eSthorpej 	uint32_t *prevpa;
4200aa15bdfSthorpej 	struct aau_desc_8 **prevp, *cur;
421c070073dSthorpej 	int ninputs = dreq->dreq_assignment->das_algdesc->dad_ninputs;
422c070073dSthorpej 	int i, error, seg;
4233b50c171Sthorpej 	size_t descsz = AAU_DESC_SIZE(ninputs);
424c070073dSthorpej 
425c070073dSthorpej 	KASSERT(ninputs <= AAU_MAX_INPUTS);
426036da55eSthorpej 
427036da55eSthorpej 	switch (dreq->dreq_outbuf_type) {
428036da55eSthorpej 	case DMOVER_BUF_LINEAR:
429036da55eSthorpej 		error = bus_dmamap_load(sc->sc_dmat, dmamap,
430036da55eSthorpej 		    dreq->dreq_outbuf.dmbuf_linear.l_addr,
431036da55eSthorpej 		    dreq->dreq_outbuf.dmbuf_linear.l_len, NULL,
432c070073dSthorpej 		    BUS_DMA_NOWAIT|BUS_DMA_READ|BUS_DMA_STREAMING);
433036da55eSthorpej 		break;
434036da55eSthorpej 
435036da55eSthorpej 	case DMOVER_BUF_UIO:
436036da55eSthorpej 	    {
437036da55eSthorpej 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
438036da55eSthorpej 
439036da55eSthorpej 		if (uio->uio_rw != UIO_READ)
440036da55eSthorpej 			return (EINVAL);
441036da55eSthorpej 
442036da55eSthorpej 		error = bus_dmamap_load_uio(sc->sc_dmat, dmamap,
443c070073dSthorpej 		    uio, BUS_DMA_NOWAIT|BUS_DMA_READ|BUS_DMA_STREAMING);
444036da55eSthorpej 		break;
445036da55eSthorpej 	    }
44690044065Sthorpej 
44790044065Sthorpej 	default:
44890044065Sthorpej 		error = EINVAL;
449036da55eSthorpej 	}
450036da55eSthorpej 
451036da55eSthorpej 	if (__predict_false(error != 0))
452036da55eSthorpej 		return (error);
453036da55eSthorpej 
454036da55eSthorpej 	switch (dreq->dreq_inbuf_type) {
455036da55eSthorpej 	case DMOVER_BUF_LINEAR:
456c070073dSthorpej 		for (i = 0; i < ninputs; i++) {
457c070073dSthorpej 			error = bus_dmamap_load(sc->sc_dmat, inmap[i],
458c070073dSthorpej 			    dreq->dreq_inbuf[i].dmbuf_linear.l_addr,
459c070073dSthorpej 			    dreq->dreq_inbuf[i].dmbuf_linear.l_len, NULL,
460c070073dSthorpej 			    BUS_DMA_NOWAIT|BUS_DMA_WRITE|BUS_DMA_STREAMING);
461c070073dSthorpej 			if (__predict_false(error != 0))
462c070073dSthorpej 				break;
463c070073dSthorpej 			if (dmamap->dm_nsegs != inmap[i]->dm_nsegs) {
464c070073dSthorpej 				error = EFAULT;	/* "address error", sort of. */
465c070073dSthorpej 				bus_dmamap_unload(sc->sc_dmat, inmap[i]);
466c070073dSthorpej 				break;
467c070073dSthorpej 			}
468c070073dSthorpej 		}
469036da55eSthorpej 		break;
470036da55eSthorpej 
471036da55eSthorpej 	 case DMOVER_BUF_UIO:
472036da55eSthorpej 	     {
473c070073dSthorpej 		struct uio *uio;
474c070073dSthorpej 
475c070073dSthorpej 		for (i = 0; i < ninputs; i++) {
476c070073dSthorpej 			uio = dreq->dreq_inbuf[i].dmbuf_uio;
477036da55eSthorpej 
478036da55eSthorpej 			if (uio->uio_rw != UIO_WRITE) {
479036da55eSthorpej 				error = EINVAL;
480036da55eSthorpej 				break;
481036da55eSthorpej 			}
482036da55eSthorpej 
483c070073dSthorpej 			error = bus_dmamap_load_uio(sc->sc_dmat, inmap[i], uio,
484c070073dSthorpej 			    BUS_DMA_NOWAIT|BUS_DMA_WRITE|BUS_DMA_STREAMING);
485c070073dSthorpej 			if (__predict_false(error != 0)) {
486c070073dSthorpej 				break;
487c070073dSthorpej 			}
488c070073dSthorpej 			if (dmamap->dm_nsegs != inmap[i]->dm_nsegs) {
489c070073dSthorpej 				error = EFAULT;	/* "address error", sort of. */
490c070073dSthorpej 				bus_dmamap_unload(sc->sc_dmat, inmap[i]);
491c070073dSthorpej 				break;
492c070073dSthorpej 			}
493c070073dSthorpej 		}
494036da55eSthorpej 		break;
495036da55eSthorpej 	    }
49690044065Sthorpej 
49790044065Sthorpej 	default:
498b9e31106Smatt 		i = 0;	/* XXX: gcc */
49990044065Sthorpej 		error = EINVAL;
500036da55eSthorpej 	}
501036da55eSthorpej 
502036da55eSthorpej 	if (__predict_false(error != 0)) {
503c070073dSthorpej 		for (--i; i >= 0; i--)
504c070073dSthorpej 			bus_dmamap_unload(sc->sc_dmat, inmap[i]);
505036da55eSthorpej 		bus_dmamap_unload(sc->sc_dmat, dmamap);
506036da55eSthorpej 		return (error);
507036da55eSthorpej 	}
508036da55eSthorpej 
509036da55eSthorpej 	bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
510036da55eSthorpej 	    BUS_DMASYNC_PREREAD);
511c070073dSthorpej 	for (i = 0; i < ninputs; i++) {
512c070073dSthorpej 		bus_dmamap_sync(sc->sc_dmat, inmap[i], 0, inmap[i]->dm_mapsize,
513c070073dSthorpej 		    BUS_DMASYNC_PREWRITE);
514c070073dSthorpej 	}
515036da55eSthorpej 
5160aa15bdfSthorpej 	prevp = (struct aau_desc_8 **) &sc->sc_firstdesc;
517036da55eSthorpej 	prevpa = &sc->sc_firstdesc_pa;
518036da55eSthorpej 
519b9e31106Smatt 	cur = NULL;	/* XXX: gcc */
520036da55eSthorpej 	for (seg = 0; seg < dmamap->dm_nsegs; seg++) {
521a39c3378Sthorpej 		cur = pool_cache_get(dc, PR_NOWAIT);
522036da55eSthorpej 		if (cur == NULL) {
523036da55eSthorpej 			*prevp = NULL;
524036da55eSthorpej 			error = ENOMEM;
525036da55eSthorpej 			goto bad;
526036da55eSthorpej 		}
527036da55eSthorpej 
528036da55eSthorpej 		*prevp = cur;
529036da55eSthorpej 		*prevpa = cur->d_pa;
530036da55eSthorpej 
531036da55eSthorpej 		prevp = &cur->d_next;
532036da55eSthorpej 		prevpa = &cur->d_nda;
533036da55eSthorpej 
534c070073dSthorpej 		for (i = 0; i < ninputs; i++) {
535036da55eSthorpej 			if (dmamap->dm_segs[seg].ds_len !=
536c070073dSthorpej 			    inmap[i]->dm_segs[seg].ds_len) {
537036da55eSthorpej 				*prevp = NULL;
538036da55eSthorpej 				error = EFAULT;	/* "address" error, sort of. */
539036da55eSthorpej 				goto bad;
540036da55eSthorpej 			}
5410aa15bdfSthorpej 			if (i < 4) {
5420aa15bdfSthorpej 				cur->d_sar[i] =
5430aa15bdfSthorpej 				    inmap[i]->dm_segs[seg].ds_addr;
5440aa15bdfSthorpej 			} else if (i < 8) {
5450aa15bdfSthorpej 				cur->d_sar5_8[i - 4] =
5460aa15bdfSthorpej 				    inmap[i]->dm_segs[seg].ds_addr;
5470aa15bdfSthorpej 			}
548c070073dSthorpej 		}
549036da55eSthorpej 		cur->d_dar = dmamap->dm_segs[seg].ds_addr;
550036da55eSthorpej 		cur->d_bc = dmamap->dm_segs[seg].ds_len;
551c070073dSthorpej 		cur->d_dc = iopaau_dc_inputs[ninputs] | AAU_DC_DWE;
5523b50c171Sthorpej 		SYNC_DESC(cur, descsz);
553036da55eSthorpej 	}
554036da55eSthorpej 
555036da55eSthorpej 	*prevp = NULL;
556036da55eSthorpej 	*prevpa = 0;
557036da55eSthorpej 
558036da55eSthorpej 	cur->d_dc |= AAU_DC_IE;
5593b50c171Sthorpej 	SYNC_DESC(cur, descsz);
560036da55eSthorpej 
561036da55eSthorpej 	sc->sc_lastdesc = cur;
562036da55eSthorpej 
563036da55eSthorpej 	return (0);
564036da55eSthorpej 
565036da55eSthorpej  bad:
566a39c3378Sthorpej 	iopaau_desc_free(dc, sc->sc_firstdesc);
567036da55eSthorpej 	bus_dmamap_unload(sc->sc_dmat, sc->sc_map_out);
568c070073dSthorpej 	for (i = 0; i < ninputs; i++)
569c070073dSthorpej 		bus_dmamap_unload(sc->sc_dmat, sc->sc_map_in[i]);
570036da55eSthorpej 	sc->sc_firstdesc = NULL;
571036da55eSthorpej 
572036da55eSthorpej 	return (error);
573036da55eSthorpej }
574036da55eSthorpej 
575036da55eSthorpej int
iopaau_intr(void * arg)576036da55eSthorpej iopaau_intr(void *arg)
577036da55eSthorpej {
578036da55eSthorpej 	struct iopaau_softc *sc = arg;
579036da55eSthorpej 	struct dmover_request *dreq;
580036da55eSthorpej 	uint32_t asr;
581036da55eSthorpej 
582036da55eSthorpej 	/* Clear the interrupt. */
583036da55eSthorpej 	asr = bus_space_read_4(sc->sc_st, sc->sc_sh, AAU_ASR);
584036da55eSthorpej 	if (asr == 0)
585036da55eSthorpej 		return (0);
586036da55eSthorpej 	bus_space_write_4(sc->sc_st, sc->sc_sh, AAU_ASR, asr);
587036da55eSthorpej 
588036da55eSthorpej 	/* XXX -- why does this happen? */
589036da55eSthorpej 	if (sc->sc_running == NULL) {
590036da55eSthorpej 		printf("%s: unexpected interrupt, ASR = 0x%08x\n",
5917490f933Smatt 		    device_xname(sc->sc_dev), asr);
592036da55eSthorpej 		return (1);
593036da55eSthorpej 	}
594036da55eSthorpej 	dreq = sc->sc_running;
595036da55eSthorpej 
596036da55eSthorpej 	/* Stop the AAU. */
597036da55eSthorpej 	bus_space_write_4(sc->sc_st, sc->sc_sh, AAU_ACR, 0);
598036da55eSthorpej 
5997490f933Smatt 	DPRINTF(("%s: got interrupt for dreq %p\n", device_xname(sc->sc_dev),
600036da55eSthorpej 	    dreq));
601036da55eSthorpej 
602036da55eSthorpej 	if (__predict_false((asr & AAU_ASR_ETIF) != 0)) {
603036da55eSthorpej 		/*
604036da55eSthorpej 		 * We expect to get end-of-chain interrupts, not
605036da55eSthorpej 		 * end-of-transfer interrupts, so panic if we get
606036da55eSthorpej 		 * one of these.
607036da55eSthorpej 		 */
608036da55eSthorpej 		panic("aau_intr: got EOT interrupt");
609036da55eSthorpej 	}
610036da55eSthorpej 
611036da55eSthorpej 	if (__predict_false((asr & AAU_ASR_MA) != 0)) {
6127490f933Smatt 		aprint_error_dev(sc->sc_dev, "WARNING: got master abort\n");
613036da55eSthorpej 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
614036da55eSthorpej 		dreq->dreq_error = EFAULT;
615036da55eSthorpej 	}
616036da55eSthorpej 
617036da55eSthorpej 	/* Finish this transfer, start next one. */
618036da55eSthorpej 	iopaau_finish(sc);
619036da55eSthorpej 
620036da55eSthorpej 	return (1);
621036da55eSthorpej }
622036da55eSthorpej 
623036da55eSthorpej void
iopaau_attach(struct iopaau_softc * sc)624036da55eSthorpej iopaau_attach(struct iopaau_softc *sc)
625036da55eSthorpej {
626036da55eSthorpej 	int error, i;
627036da55eSthorpej 
628036da55eSthorpej 	error = bus_dmamap_create(sc->sc_dmat, AAU_MAX_XFER, AAU_MAX_SEGS,
629036da55eSthorpej 	    AAU_MAX_XFER, AAU_IO_BOUNDARY, 0, &sc->sc_map_out);
630036da55eSthorpej 	if (error) {
6317490f933Smatt 		aprint_error_dev(sc->sc_dev,
6327490f933Smatt 		    "unable to create output DMA map, error = %d\n", error);
633036da55eSthorpej 		return;
634036da55eSthorpej 	}
635036da55eSthorpej 
636036da55eSthorpej 	for (i = 0; i < AAU_MAX_INPUTS; i++) {
637036da55eSthorpej 		error = bus_dmamap_create(sc->sc_dmat, AAU_MAX_XFER,
638036da55eSthorpej 		    AAU_MAX_SEGS, AAU_MAX_XFER, AAU_IO_BOUNDARY, 0,
639036da55eSthorpej 		    &sc->sc_map_in[i]);
640036da55eSthorpej 		if (error) {
6417490f933Smatt 			aprint_error_dev(sc->sc_dev,
6427490f933Smatt 			    "unable to create input %d DMA map, error = %d\n",
6437490f933Smatt 			    i, error);
644036da55eSthorpej 			return;
645036da55eSthorpej 		}
646036da55eSthorpej 	}
647036da55eSthorpej 
648036da55eSthorpej 	/*
649036da55eSthorpej 	 * Initialize global resources.  Ok to do here, since there's
650*5f9a3f32Smaxv 	 * only one AAU.  The structures are 32-byte aligned.
651036da55eSthorpej 	 */
652d18c6ca4Sad 	iopaau_desc_4_cache = pool_cache_init(sizeof(struct aau_desc_4),
653*5f9a3f32Smaxv 	    8 * 4, 0, 0, "aaud4pl",
654d18c6ca4Sad 	    NULL, IPL_VM, iopaau_desc_ctor, NULL, NULL);
655dbe854deSad 	iopaau_desc_8_cache = pool_cache_init(sizeof(struct aau_desc_8),
656*5f9a3f32Smaxv 	    8 * 4, 0, 0, "aaud8pl",
657d18c6ca4Sad 	    NULL, IPL_VM, iopaau_desc_ctor, NULL, NULL);
658036da55eSthorpej 
659036da55eSthorpej 	/* Register us with dmover. */
660036da55eSthorpej 	dmover_backend_register(&sc->sc_dmb);
661036da55eSthorpej }
662