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