1 /* $NetBSD: iopaau.c,v 1.15 2007/11/13 22:09:37 ad Exp $ */
4 * Copyright (c) 2002 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * Common code for XScale-based I/O Processor Application Accelerator
42 * The AAU provides a back-end for the dmover(9) facility.
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: iopaau.c,v 1.15 2007/11/13 22:09:37 ad Exp $");
48 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
57 #include <arm/xscale/iopaaureg.h>
58 #include <arm/xscale/iopaauvar.h>
61 #define DPRINTF(x) printf x
63 #define DPRINTF(x) /* nothing */
66 pool_cache_t iopaau_desc_4_cache
;
67 pool_cache_t iopaau_desc_8_cache
;
72 * Constructor for all types of descriptors.
75 iopaau_desc_ctor(void *arg
, void *object
, int flags
)
77 struct aau_desc_4
*d
= object
;
80 * Cache the physical address of the hardware portion of
81 * the descriptor in the software portion of the descriptor
82 * for quick reference later.
84 d
->d_pa
= vtophys((vaddr_t
)d
) + SYNC_DESC_4_OFFSET
;
85 KASSERT((d
->d_pa
& 31) == 0);
92 * Free a chain of AAU descriptors.
95 iopaau_desc_free(struct pool_cache
*dc
, void *firstdesc
)
97 struct aau_desc_4
*d
, *next
;
99 for (d
= firstdesc
; d
!= NULL
; d
= next
) {
101 pool_cache_put(dc
, d
);
108 * Start an AAU request. Must be called at splbio().
111 iopaau_start(struct iopaau_softc
*sc
)
113 struct dmover_backend
*dmb
= &sc
->sc_dmb
;
114 struct dmover_request
*dreq
;
115 struct iopaau_function
*af
;
120 KASSERT(sc
->sc_running
== NULL
);
122 dreq
= TAILQ_FIRST(&dmb
->dmb_pendreqs
);
126 dmover_backend_remque(dmb
, dreq
);
127 dreq
->dreq_flags
|= DMOVER_REQ_RUNNING
;
129 sc
->sc_running
= dreq
;
133 af
= dreq
->dreq_assignment
->das_algdesc
->dad_data
;
134 error
= (*af
->af_setup
)(sc
, dreq
);
139 dreq
->dreq_flags
|= DMOVER_REQ_ERROR
;
140 dreq
->dreq_error
= error
;
141 sc
->sc_running
= NULL
;
149 if (bus_space_read_4(sc
->sc_st
, sc
->sc_sh
, AAU_ASR
) &
151 panic("iopaau_start: AAU already active");
154 DPRINTF(("%s: starting dreq %p\n", sc
->sc_dev
.dv_xname
,
157 bus_space_write_4(sc
->sc_st
, sc
->sc_sh
, AAU_ANDAR
,
158 sc
->sc_firstdesc_pa
);
159 bus_space_write_4(sc
->sc_st
, sc
->sc_sh
, AAU_ACR
,
169 * Finish the current operation. AAU must be stopped.
172 iopaau_finish(struct iopaau_softc
*sc
)
174 struct dmover_request
*dreq
= sc
->sc_running
;
175 struct iopaau_function
*af
=
176 dreq
->dreq_assignment
->das_algdesc
->dad_data
;
177 void *firstdesc
= sc
->sc_firstdesc
;
178 int i
, ninputs
= dreq
->dreq_assignment
->das_algdesc
->dad_ninputs
;
180 sc
->sc_running
= NULL
;
182 /* If the function has inputs, unmap them. */
183 for (i
= 0; i
< ninputs
; i
++) {
184 bus_dmamap_sync(sc
->sc_dmat
, sc
->sc_map_in
[i
], 0,
185 sc
->sc_map_in
[i
]->dm_mapsize
, BUS_DMASYNC_POSTWRITE
);
186 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_map_in
[i
]);
189 /* Unload the output buffer DMA map. */
190 bus_dmamap_sync(sc
->sc_dmat
, sc
->sc_map_out
, 0,
191 sc
->sc_map_out
->dm_mapsize
, BUS_DMASYNC_POSTREAD
);
192 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_map_out
);
194 /* Get the next transfer started. */
197 /* Now free descriptors for last transfer. */
198 iopaau_desc_free(af
->af_desc_cache
, firstdesc
);
206 * Dmover back-end entry point.
209 iopaau_process(struct dmover_backend
*dmb
)
211 struct iopaau_softc
*sc
= dmb
->dmb_cookie
;
217 if (sc
->sc_running
== NULL
)
225 * iopaau_func_fill_immed_setup:
227 * Common code shared by the zero and fillN setup routines.
230 iopaau_func_fill_immed_setup(struct iopaau_softc
*sc
,
231 struct dmover_request
*dreq
, uint32_t immed
)
233 struct iopaau_function
*af
=
234 dreq
->dreq_assignment
->das_algdesc
->dad_data
;
235 struct pool_cache
*dc
= af
->af_desc_cache
;
236 bus_dmamap_t dmamap
= sc
->sc_map_out
;
238 struct aau_desc_4
**prevp
, *cur
;
241 switch (dreq
->dreq_outbuf_type
) {
242 case DMOVER_BUF_LINEAR
:
243 error
= bus_dmamap_load(sc
->sc_dmat
, dmamap
,
244 dreq
->dreq_outbuf
.dmbuf_linear
.l_addr
,
245 dreq
->dreq_outbuf
.dmbuf_linear
.l_len
, NULL
,
246 BUS_DMA_NOWAIT
|BUS_DMA_READ
|BUS_DMA_STREAMING
);
251 struct uio
*uio
= dreq
->dreq_outbuf
.dmbuf_uio
;
253 if (uio
->uio_rw
!= UIO_READ
)
256 error
= bus_dmamap_load_uio(sc
->sc_dmat
, dmamap
,
257 uio
, BUS_DMA_NOWAIT
|BUS_DMA_READ
|BUS_DMA_STREAMING
);
265 if (__predict_false(error
!= 0))
268 bus_dmamap_sync(sc
->sc_dmat
, dmamap
, 0, dmamap
->dm_mapsize
,
269 BUS_DMASYNC_PREREAD
);
271 prevp
= (struct aau_desc_4
**) &sc
->sc_firstdesc
;
272 prevpa
= &sc
->sc_firstdesc_pa
;
274 cur
= NULL
; /* XXX: gcc */
275 for (seg
= 0; seg
< dmamap
->dm_nsegs
; seg
++) {
276 cur
= pool_cache_get(dc
, PR_NOWAIT
);
286 prevp
= &cur
->d_next
;
287 prevpa
= &cur
->d_nda
;
290 * We don't actually enforce the page alignment
291 * constraint, here, because there is only one
292 * data stream to worry about.
295 cur
->d_sar
[0] = immed
;
296 cur
->d_dar
= dmamap
->dm_segs
[seg
].ds_addr
;
297 cur
->d_bc
= dmamap
->dm_segs
[seg
].ds_len
;
298 cur
->d_dc
= AAU_DC_B1_CC(AAU_DC_CC_FILL
) | AAU_DC_DWE
;
299 SYNC_DESC(cur
, sizeof(struct aau_desc_4
));
305 cur
->d_dc
|= AAU_DC_IE
;
306 SYNC_DESC(cur
, sizeof(struct aau_desc_4
));
308 sc
->sc_lastdesc
= cur
;
313 iopaau_desc_free(dc
, sc
->sc_firstdesc
);
314 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_map_out
);
315 sc
->sc_firstdesc
= NULL
;
321 * iopaau_func_zero_setup:
323 * Setup routine for the "zero" function.
326 iopaau_func_zero_setup(struct iopaau_softc
*sc
, struct dmover_request
*dreq
)
329 return (iopaau_func_fill_immed_setup(sc
, dreq
, 0));
333 * iopaau_func_fill8_setup:
335 * Setup routine for the "fill8" function.
338 iopaau_func_fill8_setup(struct iopaau_softc
*sc
, struct dmover_request
*dreq
)
341 return (iopaau_func_fill_immed_setup(sc
, dreq
,
342 dreq
->dreq_immediate
[0] |
343 (dreq
->dreq_immediate
[0] << 8) |
344 (dreq
->dreq_immediate
[0] << 16) |
345 (dreq
->dreq_immediate
[0] << 24)));
349 * Descriptor command words for varying numbers of inputs. For 1 input,
350 * this does a copy. For multiple inputs, we're doing an XOR. In this
351 * case, the first block is a "direct fill" to load the store queue, and
352 * the remaining blocks are XOR'd to the store queue.
354 static const uint32_t iopaau_dc_inputs
[] = {
357 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
), /* 1 */
359 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)| /* 2 */
360 AAU_DC_B2_CC(AAU_DC_CC_XOR
),
362 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)| /* 3 */
363 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
364 AAU_DC_B3_CC(AAU_DC_CC_XOR
),
366 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)| /* 4 */
367 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
368 AAU_DC_B3_CC(AAU_DC_CC_XOR
)|
369 AAU_DC_B4_CC(AAU_DC_CC_XOR
),
371 AAU_DC_SBCI_5_8
| /* 5 */
372 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)|
373 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
374 AAU_DC_B3_CC(AAU_DC_CC_XOR
)|
375 AAU_DC_B4_CC(AAU_DC_CC_XOR
)|
376 AAU_DC_B5_CC(AAU_DC_CC_XOR
),
378 AAU_DC_SBCI_5_8
| /* 6 */
379 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)|
380 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
381 AAU_DC_B3_CC(AAU_DC_CC_XOR
)|
382 AAU_DC_B4_CC(AAU_DC_CC_XOR
)|
383 AAU_DC_B5_CC(AAU_DC_CC_XOR
)|
384 AAU_DC_B6_CC(AAU_DC_CC_XOR
),
386 AAU_DC_SBCI_5_8
| /* 7 */
387 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)|
388 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
389 AAU_DC_B3_CC(AAU_DC_CC_XOR
)|
390 AAU_DC_B4_CC(AAU_DC_CC_XOR
)|
391 AAU_DC_B5_CC(AAU_DC_CC_XOR
)|
392 AAU_DC_B6_CC(AAU_DC_CC_XOR
)|
393 AAU_DC_B7_CC(AAU_DC_CC_XOR
),
395 AAU_DC_SBCI_5_8
| /* 8 */
396 AAU_DC_B1_CC(AAU_DC_CC_DIRECT_FILL
)|
397 AAU_DC_B2_CC(AAU_DC_CC_XOR
)|
398 AAU_DC_B3_CC(AAU_DC_CC_XOR
)|
399 AAU_DC_B4_CC(AAU_DC_CC_XOR
)|
400 AAU_DC_B5_CC(AAU_DC_CC_XOR
)|
401 AAU_DC_B6_CC(AAU_DC_CC_XOR
)|
402 AAU_DC_B7_CC(AAU_DC_CC_XOR
)|
403 AAU_DC_B8_CC(AAU_DC_CC_XOR
),
407 * iopaau_func_xor_setup:
409 * Setup routine for the "copy", "xor2".."xor8" functions.
412 iopaau_func_xor_setup(struct iopaau_softc
*sc
, struct dmover_request
*dreq
)
414 struct iopaau_function
*af
=
415 dreq
->dreq_assignment
->das_algdesc
->dad_data
;
416 struct pool_cache
*dc
= af
->af_desc_cache
;
417 bus_dmamap_t dmamap
= sc
->sc_map_out
;
418 bus_dmamap_t
*inmap
= sc
->sc_map_in
;
420 struct aau_desc_8
**prevp
, *cur
;
421 int ninputs
= dreq
->dreq_assignment
->das_algdesc
->dad_ninputs
;
423 size_t descsz
= AAU_DESC_SIZE(ninputs
);
425 KASSERT(ninputs
<= AAU_MAX_INPUTS
);
427 switch (dreq
->dreq_outbuf_type
) {
428 case DMOVER_BUF_LINEAR
:
429 error
= bus_dmamap_load(sc
->sc_dmat
, dmamap
,
430 dreq
->dreq_outbuf
.dmbuf_linear
.l_addr
,
431 dreq
->dreq_outbuf
.dmbuf_linear
.l_len
, NULL
,
432 BUS_DMA_NOWAIT
|BUS_DMA_READ
|BUS_DMA_STREAMING
);
437 struct uio
*uio
= dreq
->dreq_outbuf
.dmbuf_uio
;
439 if (uio
->uio_rw
!= UIO_READ
)
442 error
= bus_dmamap_load_uio(sc
->sc_dmat
, dmamap
,
443 uio
, BUS_DMA_NOWAIT
|BUS_DMA_READ
|BUS_DMA_STREAMING
);
451 if (__predict_false(error
!= 0))
454 switch (dreq
->dreq_inbuf_type
) {
455 case DMOVER_BUF_LINEAR
:
456 for (i
= 0; i
< ninputs
; i
++) {
457 error
= bus_dmamap_load(sc
->sc_dmat
, inmap
[i
],
458 dreq
->dreq_inbuf
[i
].dmbuf_linear
.l_addr
,
459 dreq
->dreq_inbuf
[i
].dmbuf_linear
.l_len
, NULL
,
460 BUS_DMA_NOWAIT
|BUS_DMA_WRITE
|BUS_DMA_STREAMING
);
461 if (__predict_false(error
!= 0))
463 if (dmamap
->dm_nsegs
!= inmap
[i
]->dm_nsegs
) {
464 error
= EFAULT
; /* "address error", sort of. */
465 bus_dmamap_unload(sc
->sc_dmat
, inmap
[i
]);
475 for (i
= 0; i
< ninputs
; i
++) {
476 uio
= dreq
->dreq_inbuf
[i
].dmbuf_uio
;
478 if (uio
->uio_rw
!= UIO_WRITE
) {
483 error
= bus_dmamap_load_uio(sc
->sc_dmat
, inmap
[i
], uio
,
484 BUS_DMA_NOWAIT
|BUS_DMA_WRITE
|BUS_DMA_STREAMING
);
485 if (__predict_false(error
!= 0)) {
488 if (dmamap
->dm_nsegs
!= inmap
[i
]->dm_nsegs
) {
489 error
= EFAULT
; /* "address error", sort of. */
490 bus_dmamap_unload(sc
->sc_dmat
, inmap
[i
]);
498 i
= 0; /* XXX: gcc */
502 if (__predict_false(error
!= 0)) {
503 for (--i
; i
>= 0; i
--)
504 bus_dmamap_unload(sc
->sc_dmat
, inmap
[i
]);
505 bus_dmamap_unload(sc
->sc_dmat
, dmamap
);
509 bus_dmamap_sync(sc
->sc_dmat
, dmamap
, 0, dmamap
->dm_mapsize
,
510 BUS_DMASYNC_PREREAD
);
511 for (i
= 0; i
< ninputs
; i
++) {
512 bus_dmamap_sync(sc
->sc_dmat
, inmap
[i
], 0, inmap
[i
]->dm_mapsize
,
513 BUS_DMASYNC_PREWRITE
);
516 prevp
= (struct aau_desc_8
**) &sc
->sc_firstdesc
;
517 prevpa
= &sc
->sc_firstdesc_pa
;
519 cur
= NULL
; /* XXX: gcc */
520 for (seg
= 0; seg
< dmamap
->dm_nsegs
; seg
++) {
521 cur
= pool_cache_get(dc
, PR_NOWAIT
);
531 prevp
= &cur
->d_next
;
532 prevpa
= &cur
->d_nda
;
534 for (i
= 0; i
< ninputs
; i
++) {
535 if (dmamap
->dm_segs
[seg
].ds_len
!=
536 inmap
[i
]->dm_segs
[seg
].ds_len
) {
538 error
= EFAULT
; /* "address" error, sort of. */
543 inmap
[i
]->dm_segs
[seg
].ds_addr
;
545 cur
->d_sar5_8
[i
- 4] =
546 inmap
[i
]->dm_segs
[seg
].ds_addr
;
549 cur
->d_dar
= dmamap
->dm_segs
[seg
].ds_addr
;
550 cur
->d_bc
= dmamap
->dm_segs
[seg
].ds_len
;
551 cur
->d_dc
= iopaau_dc_inputs
[ninputs
] | AAU_DC_DWE
;
552 SYNC_DESC(cur
, descsz
);
558 cur
->d_dc
|= AAU_DC_IE
;
559 SYNC_DESC(cur
, descsz
);
561 sc
->sc_lastdesc
= cur
;
566 iopaau_desc_free(dc
, sc
->sc_firstdesc
);
567 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_map_out
);
568 for (i
= 0; i
< ninputs
; i
++)
569 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_map_in
[i
]);
570 sc
->sc_firstdesc
= NULL
;
576 iopaau_intr(void *arg
)
578 struct iopaau_softc
*sc
= arg
;
579 struct dmover_request
*dreq
;
582 /* Clear the interrupt. */
583 asr
= bus_space_read_4(sc
->sc_st
, sc
->sc_sh
, AAU_ASR
);
586 bus_space_write_4(sc
->sc_st
, sc
->sc_sh
, AAU_ASR
, asr
);
588 /* XXX -- why does this happen? */
589 if (sc
->sc_running
== NULL
) {
590 printf("%s: unexpected interrupt, ASR = 0x%08x\n",
591 sc
->sc_dev
.dv_xname
, asr
);
594 dreq
= sc
->sc_running
;
597 bus_space_write_4(sc
->sc_st
, sc
->sc_sh
, AAU_ACR
, 0);
599 DPRINTF(("%s: got interrupt for dreq %p\n", sc
->sc_dev
.dv_xname
,
602 if (__predict_false((asr
& AAU_ASR_ETIF
) != 0)) {
604 * We expect to get end-of-chain interrupts, not
605 * end-of-transfer interrupts, so panic if we get
608 panic("aau_intr: got EOT interrupt");
611 if (__predict_false((asr
& AAU_ASR_MA
) != 0)) {
612 printf("%s: WARNING: got master abort\n", sc
->sc_dev
.dv_xname
);
613 dreq
->dreq_flags
|= DMOVER_REQ_ERROR
;
614 dreq
->dreq_error
= EFAULT
;
617 /* Finish this transfer, start next one. */
624 iopaau_attach(struct iopaau_softc
*sc
)
628 error
= bus_dmamap_create(sc
->sc_dmat
, AAU_MAX_XFER
, AAU_MAX_SEGS
,
629 AAU_MAX_XFER
, AAU_IO_BOUNDARY
, 0, &sc
->sc_map_out
);
632 "%s: unable to create output DMA map, error = %d\n",
633 sc
->sc_dev
.dv_xname
, error
);
637 for (i
= 0; i
< AAU_MAX_INPUTS
; i
++) {
638 error
= bus_dmamap_create(sc
->sc_dmat
, AAU_MAX_XFER
,
639 AAU_MAX_SEGS
, AAU_MAX_XFER
, AAU_IO_BOUNDARY
, 0,
642 aprint_error("%s: unable to create input %d DMA map, "
643 "error = %d\n", sc
->sc_dev
.dv_xname
, i
, error
);
649 * Initialize global resources. Ok to do here, since there's
652 iopaau_desc_4_cache
= pool_cache_init(sizeof(struct aau_desc_4
),
653 8 * 4, offsetof(struct aau_desc_4
, d_nda
), 0, "aaud4pl",
654 NULL
, IPL_VM
, iopaau_desc_ctor
, NULL
, NULL
);
655 iopaau_desc_8_cache
= pool_cache_init(sizeof(struct aau_desc_8
),
656 8 * 4, offsetof(struct aau_desc_8
, d_nda
), 0, "aaud8pl",
657 NULL
, IPL_VM
, iopaau_desc_ctor
, NULL
, NULL
);
659 /* Register us with dmover. */
660 dmover_backend_register(&sc
->sc_dmb
);