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