1*abdf7c82Sjmcneill /* $NetBSD: exi.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $ */
2c0f93ec1Sjmcneill
3c0f93ec1Sjmcneill /*-
4c0f93ec1Sjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca>
5c0f93ec1Sjmcneill * All rights reserved.
6c0f93ec1Sjmcneill *
7c0f93ec1Sjmcneill * Redistribution and use in source and binary forms, with or without
8c0f93ec1Sjmcneill * modification, are permitted provided that the following conditions
9c0f93ec1Sjmcneill * are met:
10c0f93ec1Sjmcneill * 1. Redistributions of source code must retain the above copyright
11c0f93ec1Sjmcneill * notice, this list of conditions and the following disclaimer.
12c0f93ec1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13c0f93ec1Sjmcneill * notice, this list of conditions and the following disclaimer in the
14c0f93ec1Sjmcneill * documentation and/or other materials provided with the distribution.
15c0f93ec1Sjmcneill *
16c0f93ec1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17c0f93ec1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18c0f93ec1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19c0f93ec1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20c0f93ec1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21c0f93ec1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22c0f93ec1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23c0f93ec1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24c0f93ec1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c0f93ec1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c0f93ec1Sjmcneill * SUCH DAMAGE.
27c0f93ec1Sjmcneill */
28c0f93ec1Sjmcneill
29c0f93ec1Sjmcneill #include <sys/cdefs.h>
30*abdf7c82Sjmcneill __KERNEL_RCSID(0, "$NetBSD: exi.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $");
31c0f93ec1Sjmcneill
32c0f93ec1Sjmcneill #include <sys/param.h>
33c0f93ec1Sjmcneill #include <sys/bus.h>
34c0f93ec1Sjmcneill #include <sys/device.h>
35c0f93ec1Sjmcneill #include <sys/systm.h>
36c0f93ec1Sjmcneill #include <sys/bitops.h>
37c0f93ec1Sjmcneill #include <sys/mutex.h>
38c0f93ec1Sjmcneill #include <uvm/uvm_extern.h>
39c0f93ec1Sjmcneill
40c0f93ec1Sjmcneill #include <machine/wii.h>
41c0f93ec1Sjmcneill #include <machine/pio.h>
42c0f93ec1Sjmcneill
43c0f93ec1Sjmcneill #include "locators.h"
44c0f93ec1Sjmcneill #include "mainbus.h"
45c0f93ec1Sjmcneill #include "exi.h"
46c0f93ec1Sjmcneill
47c0f93ec1Sjmcneill #define EXI_NUM_CHAN 3
48c0f93ec1Sjmcneill #define EXI_NUM_DEV 3
49c0f93ec1Sjmcneill
50c0f93ec1Sjmcneill /* This is an arbitrary limit. The real limit is probably much higher. */
51c0f93ec1Sjmcneill #define EXI_MAX_DMA 4096
52c0f93ec1Sjmcneill
53c0f93ec1Sjmcneill #define EXI_CSR(n) (0x00 + (n) * 0x14)
54c0f93ec1Sjmcneill #define EXI_CSR_CS __BITS(9,7)
55*abdf7c82Sjmcneill #define EXI_CSR_CLK __BITS(6,4)
56c0f93ec1Sjmcneill #define EXI_MAR(n) (0x04 + (n) * 0x14)
57c0f93ec1Sjmcneill #define EXI_LENGTH(n) (0x08 + (n) * 0x14)
58c0f93ec1Sjmcneill #define EXI_CR(n) (0x0c + (n) * 0x14)
59c0f93ec1Sjmcneill #define EXI_CR_TLEN __BITS(5,4)
60c0f93ec1Sjmcneill #define EXI_CR_RW __BITS(3,2)
61c0f93ec1Sjmcneill #define EXI_CR_RW_READ __SHIFTIN(0, EXI_CR_RW)
62c0f93ec1Sjmcneill #define EXI_CR_RW_WRITE __SHIFTIN(1, EXI_CR_RW)
63c0f93ec1Sjmcneill #define EXI_CR_DMA __BIT(1)
64c0f93ec1Sjmcneill #define EXI_CR_TSTART __BIT(0)
65c0f93ec1Sjmcneill #define EXI_DATA(n) (0x10 + (n) * 0x14)
66c0f93ec1Sjmcneill
67c0f93ec1Sjmcneill #define ASSERT_CHAN_VALID(chan) KASSERT((chan) >= 0 && (chan) < EXI_NUM_CHAN)
68c0f93ec1Sjmcneill #define ASSERT_DEV_VALID(dev) KASSERT((dev) >= 0 && (dev) < EXI_NUM_DEV)
69c0f93ec1Sjmcneill #define ASSERT_LEN_VALID(len) KASSERT((len) == 1 || (len) == 2 || (len) == 4)
70c0f93ec1Sjmcneill
71c0f93ec1Sjmcneill struct exi_channel {
72c0f93ec1Sjmcneill kmutex_t ch_lock;
73c0f93ec1Sjmcneill
74c0f93ec1Sjmcneill bus_dmamap_t ch_dmamap;
75c0f93ec1Sjmcneill
76c0f93ec1Sjmcneill device_t ch_child[EXI_NUM_DEV];
77c0f93ec1Sjmcneill };
78c0f93ec1Sjmcneill
79c0f93ec1Sjmcneill struct exi_softc {
80c0f93ec1Sjmcneill device_t sc_dev;
81c0f93ec1Sjmcneill bus_space_tag_t sc_bst;
82c0f93ec1Sjmcneill bus_space_handle_t sc_bsh;
83c0f93ec1Sjmcneill bus_dma_tag_t sc_dmat;
84c0f93ec1Sjmcneill
85c0f93ec1Sjmcneill struct exi_channel sc_chan[EXI_NUM_CHAN];
86c0f93ec1Sjmcneill };
87c0f93ec1Sjmcneill
88c0f93ec1Sjmcneill static struct exi_softc *exi_softc;
89c0f93ec1Sjmcneill
90c0f93ec1Sjmcneill #define RD4(sc, reg) \
91c0f93ec1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
92c0f93ec1Sjmcneill #define WR4(sc, reg, val) \
93c0f93ec1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
94c0f93ec1Sjmcneill
95c0f93ec1Sjmcneill static int exi_match(device_t, cfdata_t, void *);
96c0f93ec1Sjmcneill static void exi_attach(device_t, device_t, void *);
97c0f93ec1Sjmcneill
98c0f93ec1Sjmcneill static int exi_rescan(device_t, const char *, const int *);
99c0f93ec1Sjmcneill static int exi_print(void *, const char *);
100c0f93ec1Sjmcneill
101c0f93ec1Sjmcneill CFATTACH_DECL_NEW(exi, sizeof(struct exi_softc),
102c0f93ec1Sjmcneill exi_match, exi_attach, NULL, NULL);
103c0f93ec1Sjmcneill
104c0f93ec1Sjmcneill static int
exi_match(device_t parent,cfdata_t cf,void * aux)105c0f93ec1Sjmcneill exi_match(device_t parent, cfdata_t cf, void *aux)
106c0f93ec1Sjmcneill {
107c0f93ec1Sjmcneill struct mainbus_attach_args *maa = aux;
108c0f93ec1Sjmcneill
109c0f93ec1Sjmcneill return strcmp(maa->maa_name, "exi") == 0;
110c0f93ec1Sjmcneill }
111c0f93ec1Sjmcneill
112c0f93ec1Sjmcneill static void
exi_attach(device_t parent,device_t self,void * aux)113c0f93ec1Sjmcneill exi_attach(device_t parent, device_t self, void *aux)
114c0f93ec1Sjmcneill {
115c0f93ec1Sjmcneill struct mainbus_attach_args * const maa = aux;
116c0f93ec1Sjmcneill struct exi_softc * const sc = device_private(self);
117c0f93ec1Sjmcneill uint8_t chan;
118c0f93ec1Sjmcneill int error;
119c0f93ec1Sjmcneill
120c0f93ec1Sjmcneill KASSERT(device_unit(self) == 0);
121c0f93ec1Sjmcneill
122c0f93ec1Sjmcneill aprint_naive("\n");
123c0f93ec1Sjmcneill aprint_normal(": External Interface\n");
124c0f93ec1Sjmcneill
125c0f93ec1Sjmcneill exi_softc = sc;
126c0f93ec1Sjmcneill sc->sc_dev = self;
127c0f93ec1Sjmcneill sc->sc_bst = maa->maa_bst;
128c0f93ec1Sjmcneill if (bus_space_map(sc->sc_bst, maa->maa_addr, EXI_SIZE, 0,
129c0f93ec1Sjmcneill &sc->sc_bsh) != 0) {
130c0f93ec1Sjmcneill aprint_error_dev(self, "couldn't map registers\n");
131c0f93ec1Sjmcneill return;
132c0f93ec1Sjmcneill }
133c0f93ec1Sjmcneill sc->sc_dmat = maa->maa_dmat;
134c0f93ec1Sjmcneill for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
135c0f93ec1Sjmcneill mutex_init(&sc->sc_chan[chan].ch_lock, MUTEX_DEFAULT, IPL_VM);
136c0f93ec1Sjmcneill error = bus_dmamap_create(exi_softc->sc_dmat, EXI_MAX_DMA, 1,
137c0f93ec1Sjmcneill EXI_MAX_DMA, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
138c0f93ec1Sjmcneill &sc->sc_chan[chan].ch_dmamap);
139c0f93ec1Sjmcneill if (error != 0) {
140c0f93ec1Sjmcneill aprint_error_dev(self, "couldn't create dmamap: %d\n",
141c0f93ec1Sjmcneill error);
142c0f93ec1Sjmcneill return;
143c0f93ec1Sjmcneill }
144c0f93ec1Sjmcneill }
145c0f93ec1Sjmcneill
146c0f93ec1Sjmcneill exi_rescan(self, NULL, NULL);
147c0f93ec1Sjmcneill }
148c0f93ec1Sjmcneill
149c0f93ec1Sjmcneill static int
exi_rescan(device_t self,const char * ifattr,const int * locs)150c0f93ec1Sjmcneill exi_rescan(device_t self, const char *ifattr, const int *locs)
151c0f93ec1Sjmcneill {
152c0f93ec1Sjmcneill struct exi_softc * const sc = device_private(self);
153c0f93ec1Sjmcneill uint8_t chan, dev;
154c0f93ec1Sjmcneill
155c0f93ec1Sjmcneill for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
156c0f93ec1Sjmcneill struct exi_channel *ch = &sc->sc_chan[chan];
157c0f93ec1Sjmcneill for (dev = 0; dev < EXI_NUM_DEV; dev++) {
158c0f93ec1Sjmcneill struct exi_attach_args eaa = {};
159c0f93ec1Sjmcneill uint16_t command = 0x0000; /* ID command */
160c0f93ec1Sjmcneill uint32_t id = 0;
161c0f93ec1Sjmcneill
162c0f93ec1Sjmcneill if (ch->ch_child[dev] != NULL) {
163c0f93ec1Sjmcneill continue;
164c0f93ec1Sjmcneill }
165c0f93ec1Sjmcneill
166*abdf7c82Sjmcneill exi_select(chan, dev, EXI_FREQ_8MHZ);
167c0f93ec1Sjmcneill exi_send_imm(chan, dev, &command, sizeof(command));
168c0f93ec1Sjmcneill exi_recv_imm(chan, dev, &id, sizeof(id));
169c0f93ec1Sjmcneill exi_unselect(chan);
170c0f93ec1Sjmcneill
171c0f93ec1Sjmcneill if (id == 0 || id == 0xffffffff) {
172c0f93ec1Sjmcneill continue;
173c0f93ec1Sjmcneill }
174c0f93ec1Sjmcneill
175c0f93ec1Sjmcneill eaa.eaa_id = id;
176c0f93ec1Sjmcneill eaa.eaa_chan = chan;
177c0f93ec1Sjmcneill eaa.eaa_device = dev;
178c0f93ec1Sjmcneill
179c0f93ec1Sjmcneill ch->ch_child[dev] = config_found(self, &eaa, exi_print,
180c0f93ec1Sjmcneill CFARGS(.submatch = config_stdsubmatch,
181c0f93ec1Sjmcneill .locators = locs));
182c0f93ec1Sjmcneill }
183c0f93ec1Sjmcneill }
184c0f93ec1Sjmcneill
185c0f93ec1Sjmcneill return 0;
186c0f93ec1Sjmcneill }
187c0f93ec1Sjmcneill
188c0f93ec1Sjmcneill static int
exi_print(void * aux,const char * pnp)189c0f93ec1Sjmcneill exi_print(void *aux, const char *pnp)
190c0f93ec1Sjmcneill {
191c0f93ec1Sjmcneill struct exi_attach_args *eaa = aux;
192c0f93ec1Sjmcneill
193c0f93ec1Sjmcneill if (pnp != NULL) {
194c0f93ec1Sjmcneill aprint_normal("EXI device 0x%08x at %s", eaa->eaa_id, pnp);
195c0f93ec1Sjmcneill }
196c0f93ec1Sjmcneill
197c0f93ec1Sjmcneill aprint_normal(" addr %u-%u", eaa->eaa_chan, eaa->eaa_device);
198c0f93ec1Sjmcneill
199c0f93ec1Sjmcneill return UNCONF;
200c0f93ec1Sjmcneill }
201c0f93ec1Sjmcneill
202c0f93ec1Sjmcneill void
exi_select(uint8_t chan,uint8_t dev,exi_freq_t freq)203*abdf7c82Sjmcneill exi_select(uint8_t chan, uint8_t dev, exi_freq_t freq)
204c0f93ec1Sjmcneill {
205c0f93ec1Sjmcneill struct exi_channel *ch;
206c0f93ec1Sjmcneill uint32_t val;
207c0f93ec1Sjmcneill
208c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
209c0f93ec1Sjmcneill ASSERT_DEV_VALID(dev);
210c0f93ec1Sjmcneill
211c0f93ec1Sjmcneill ch = &exi_softc->sc_chan[chan];
212c0f93ec1Sjmcneill mutex_enter(&ch->ch_lock);
213c0f93ec1Sjmcneill
214c0f93ec1Sjmcneill val = RD4(exi_softc, EXI_CSR(chan));
215c0f93ec1Sjmcneill val &= ~EXI_CSR_CS;
216c0f93ec1Sjmcneill val |= __SHIFTIN(__BIT(dev), EXI_CSR_CS);
217*abdf7c82Sjmcneill val &= ~EXI_CSR_CLK;
218*abdf7c82Sjmcneill val |= __SHIFTIN(freq, EXI_CSR_CLK);
219c0f93ec1Sjmcneill WR4(exi_softc, EXI_CSR(chan), val);
220c0f93ec1Sjmcneill }
221c0f93ec1Sjmcneill
222c0f93ec1Sjmcneill void
exi_unselect(uint8_t chan)223c0f93ec1Sjmcneill exi_unselect(uint8_t chan)
224c0f93ec1Sjmcneill {
225c0f93ec1Sjmcneill struct exi_channel *ch;
226c0f93ec1Sjmcneill uint32_t val;
227c0f93ec1Sjmcneill
228c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
229c0f93ec1Sjmcneill
230c0f93ec1Sjmcneill ch = &exi_softc->sc_chan[chan];
231c0f93ec1Sjmcneill
232c0f93ec1Sjmcneill val = RD4(exi_softc, EXI_CSR(chan));
233c0f93ec1Sjmcneill val &= ~EXI_CSR_CS;
234c0f93ec1Sjmcneill WR4(exi_softc, EXI_CSR(chan), val);
235c0f93ec1Sjmcneill
236c0f93ec1Sjmcneill mutex_exit(&ch->ch_lock);
237c0f93ec1Sjmcneill }
238c0f93ec1Sjmcneill
239c0f93ec1Sjmcneill static void
exi_wait(uint8_t chan)240c0f93ec1Sjmcneill exi_wait(uint8_t chan)
241c0f93ec1Sjmcneill {
242c0f93ec1Sjmcneill uint32_t val;
243c0f93ec1Sjmcneill
244c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
245c0f93ec1Sjmcneill
246c0f93ec1Sjmcneill do {
247c0f93ec1Sjmcneill val = RD4(exi_softc, EXI_CR(chan));
248c0f93ec1Sjmcneill } while ((val & EXI_CR_TSTART) != 0);
249c0f93ec1Sjmcneill }
250c0f93ec1Sjmcneill
251c0f93ec1Sjmcneill void
exi_send_imm(uint8_t chan,uint8_t dev,const void * data,size_t datalen)252c0f93ec1Sjmcneill exi_send_imm(uint8_t chan, uint8_t dev, const void *data, size_t datalen)
253c0f93ec1Sjmcneill {
254c0f93ec1Sjmcneill struct exi_channel *ch;
255c0f93ec1Sjmcneill uint32_t val = 0;
256c0f93ec1Sjmcneill
257c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
258c0f93ec1Sjmcneill ASSERT_DEV_VALID(dev);
259c0f93ec1Sjmcneill ASSERT_LEN_VALID(datalen);
260c0f93ec1Sjmcneill
261c0f93ec1Sjmcneill ch = &exi_softc->sc_chan[chan];
262c0f93ec1Sjmcneill KASSERT(mutex_owned(&ch->ch_lock));
263c0f93ec1Sjmcneill
264c0f93ec1Sjmcneill switch (datalen) {
265c0f93ec1Sjmcneill case 1:
266c0f93ec1Sjmcneill val = *(const uint8_t *)data << 24;
267c0f93ec1Sjmcneill break;
268c0f93ec1Sjmcneill case 2:
269c0f93ec1Sjmcneill val = *(const uint16_t *)data << 16;
270c0f93ec1Sjmcneill break;
271c0f93ec1Sjmcneill case 4:
272c0f93ec1Sjmcneill val = *(const uint32_t *)data;
273c0f93ec1Sjmcneill break;
274c0f93ec1Sjmcneill }
275c0f93ec1Sjmcneill
276c0f93ec1Sjmcneill WR4(exi_softc, EXI_DATA(chan), val);
277c0f93ec1Sjmcneill WR4(exi_softc, EXI_CR(chan),
278c0f93ec1Sjmcneill EXI_CR_TSTART | EXI_CR_RW_WRITE |
279c0f93ec1Sjmcneill __SHIFTIN(datalen - 1, EXI_CR_TLEN));
280c0f93ec1Sjmcneill exi_wait(chan);
281c0f93ec1Sjmcneill }
282c0f93ec1Sjmcneill
283c0f93ec1Sjmcneill void
exi_recv_imm(uint8_t chan,uint8_t dev,void * data,size_t datalen)284c0f93ec1Sjmcneill exi_recv_imm(uint8_t chan, uint8_t dev, void *data, size_t datalen)
285c0f93ec1Sjmcneill {
286c0f93ec1Sjmcneill struct exi_channel *ch;
287c0f93ec1Sjmcneill uint32_t val;
288c0f93ec1Sjmcneill
289c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
290c0f93ec1Sjmcneill ASSERT_DEV_VALID(dev);
291c0f93ec1Sjmcneill ASSERT_LEN_VALID(datalen);
292c0f93ec1Sjmcneill
293c0f93ec1Sjmcneill ch = &exi_softc->sc_chan[chan];
294c0f93ec1Sjmcneill KASSERT(mutex_owned(&ch->ch_lock));
295c0f93ec1Sjmcneill
296c0f93ec1Sjmcneill WR4(exi_softc, EXI_CR(chan),
297c0f93ec1Sjmcneill EXI_CR_TSTART | EXI_CR_RW_READ |
298c0f93ec1Sjmcneill __SHIFTIN(datalen - 1, EXI_CR_TLEN));
299c0f93ec1Sjmcneill exi_wait(chan);
300c0f93ec1Sjmcneill val = RD4(exi_softc, EXI_DATA(chan));
301c0f93ec1Sjmcneill
302c0f93ec1Sjmcneill switch (datalen) {
303c0f93ec1Sjmcneill case 1:
304c0f93ec1Sjmcneill *(uint8_t *)data = val >> 24;
305c0f93ec1Sjmcneill break;
306c0f93ec1Sjmcneill case 2:
307c0f93ec1Sjmcneill *(uint16_t *)data = val >> 16;
308c0f93ec1Sjmcneill break;
309c0f93ec1Sjmcneill case 4:
310c0f93ec1Sjmcneill *(uint32_t *)data = val;
311c0f93ec1Sjmcneill break;
312c0f93ec1Sjmcneill }
313c0f93ec1Sjmcneill }
314c0f93ec1Sjmcneill
315c0f93ec1Sjmcneill void
exi_recv_dma(uint8_t chan,uint8_t dev,void * data,size_t datalen)316c0f93ec1Sjmcneill exi_recv_dma(uint8_t chan, uint8_t dev, void *data, size_t datalen)
317c0f93ec1Sjmcneill {
318c0f93ec1Sjmcneill struct exi_channel *ch;
319c0f93ec1Sjmcneill int error;
320c0f93ec1Sjmcneill
321c0f93ec1Sjmcneill ASSERT_CHAN_VALID(chan);
322c0f93ec1Sjmcneill ASSERT_DEV_VALID(dev);
323c0f93ec1Sjmcneill KASSERT((datalen & 0x1f) == 0);
324c0f93ec1Sjmcneill
325c0f93ec1Sjmcneill ch = &exi_softc->sc_chan[chan];
326c0f93ec1Sjmcneill KASSERT(mutex_owned(&ch->ch_lock));
327c0f93ec1Sjmcneill
328c0f93ec1Sjmcneill error = bus_dmamap_load(exi_softc->sc_dmat, ch->ch_dmamap,
329c0f93ec1Sjmcneill data, datalen, NULL, BUS_DMA_WAITOK);
330c0f93ec1Sjmcneill if (error != 0) {
331c0f93ec1Sjmcneill device_printf(exi_softc->sc_dev, "can't load DMA handle: %d\n",
332c0f93ec1Sjmcneill error);
333c0f93ec1Sjmcneill return;
334c0f93ec1Sjmcneill }
335c0f93ec1Sjmcneill
336c0f93ec1Sjmcneill KASSERT((ch->ch_dmamap->dm_segs[0].ds_addr & 0x1f) == 0);
337c0f93ec1Sjmcneill
338c0f93ec1Sjmcneill bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
339c0f93ec1Sjmcneill BUS_DMASYNC_PREREAD);
340c0f93ec1Sjmcneill
341c0f93ec1Sjmcneill WR4(exi_softc, EXI_MAR(chan), ch->ch_dmamap->dm_segs[0].ds_addr);
342c0f93ec1Sjmcneill WR4(exi_softc, EXI_LENGTH(chan), datalen);
343c0f93ec1Sjmcneill WR4(exi_softc, EXI_CR(chan),
344c0f93ec1Sjmcneill EXI_CR_TSTART | EXI_CR_RW_READ | EXI_CR_DMA);
345c0f93ec1Sjmcneill exi_wait(chan);
346c0f93ec1Sjmcneill
347c0f93ec1Sjmcneill bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
348c0f93ec1Sjmcneill BUS_DMASYNC_POSTREAD);
349c0f93ec1Sjmcneill
350c0f93ec1Sjmcneill bus_dmamap_unload(exi_softc->sc_dmat, ch->ch_dmamap);
351c0f93ec1Sjmcneill }
352