xref: /netbsd-src/sys/arch/dreamcast/dev/maple/maple.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: maple.c,v 1.26 2003/07/15 01:31:39 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by ITOH Yasufumi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*-
40  * Copyright (c) 2001 Marcus Comstedt
41  * All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *	This product includes software developed by Marcus Comstedt.
54  * 4. Neither the name of The NetBSD Foundation nor the names of its
55  *    contributors may be used to endorse or promote products derived
56  *    from this software without specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
59  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
60  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
61  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
62  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
63  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
64  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
65  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
66  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
67  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
68  * POSSIBILITY OF SUCH DAMAGE.
69  */
70 
71 #include <sys/cdefs.h>
72 __KERNEL_RCSID(0, "$NetBSD: maple.c,v 1.26 2003/07/15 01:31:39 lukem Exp $");
73 
74 #include <sys/param.h>
75 #include <sys/device.h>
76 #include <sys/fcntl.h>
77 #include <sys/kernel.h>
78 #include <sys/kthread.h>
79 #include <sys/poll.h>
80 #include <sys/select.h>
81 #include <sys/proc.h>
82 #include <sys/signalvar.h>
83 #include <sys/systm.h>
84 #include <sys/conf.h>
85 
86 #include <uvm/uvm_extern.h>
87 
88 #include <machine/cpu.h>
89 #include <machine/bus.h>
90 #include <machine/sysasicvar.h>
91 #include <sh3/pmap.h>
92 
93 #include <dreamcast/dev/maple/maple.h>
94 #include <dreamcast/dev/maple/mapleconf.h>
95 #include <dreamcast/dev/maple/maplevar.h>
96 #include <dreamcast/dev/maple/maplereg.h>
97 #include <dreamcast/dev/maple/mapleio.h>
98 
99 #include "locators.h"
100 
101 /* Internal macros, functions, and variables. */
102 
103 #define MAPLE_CALLOUT_TICKS 2
104 
105 #define MAPLEBUSUNIT(dev)  (minor(dev)>>5)
106 #define MAPLEPORT(dev)     ((minor(dev) & 0x18) >> 3)
107 #define MAPLESUBUNIT(dev)  (minor(dev) & 0x7)
108 
109 /* interrupt priority level */
110 #define	IPL_MAPLE	IPL_BIO
111 #define splmaple()	splbio()
112 
113 /*
114  * Function declarations.
115  */
116 static int	maplematch(struct device *, struct cfdata *, void *);
117 static void	mapleattach(struct device *, struct device *, void *);
118 static void	maple_create_event_thread(void *);
119 static void	maple_scanbus(struct maple_softc *);
120 static char *	maple_unit_name(char *, int port, int subunit);
121 static void	maple_begin_txbuf(struct maple_softc *);
122 static int	maple_end_txbuf(struct maple_softc *);
123 static void	maple_queue_command(struct maple_softc *, struct maple_unit *,
124 		    int command, int datalen, const void *dataaddr);
125 static void	maple_write_command(struct maple_softc *, struct maple_unit *,
126 		    int, int, const void *);
127 static void	maple_start(struct maple_softc *sc);
128 static void	maple_start_poll(struct maple_softc *);
129 static void	maple_check_subunit_change(struct maple_softc *,
130 		    struct maple_unit *);
131 static void	maple_check_unit_change(struct maple_softc *,
132 		    struct maple_unit *);
133 static void	maple_print_unit(void *, const char *);
134 static int	maplesubmatch(struct device *, struct cfdata *, void *);
135 static int	mapleprint(void *, const char *);
136 static void	maple_attach_unit(struct maple_softc *, struct maple_unit *);
137 static void	maple_detach_unit_nofix(struct maple_softc *,
138 		    struct maple_unit *);
139 static void	maple_detach_unit(struct maple_softc *, struct maple_unit *);
140 static void	maple_queue_cmds(struct maple_softc *,
141 		    struct maple_cmdq_head *);
142 static void	maple_unit_probe(struct maple_softc *);
143 static void	maple_unit_ping(struct maple_softc *);
144 static int	maple_send_defered_periodic(struct maple_softc *);
145 static void	maple_send_periodic(struct maple_softc *);
146 static void	maple_remove_from_queues(struct maple_softc *,
147 		    struct maple_unit *);
148 static int	maple_retry(struct maple_softc *, struct maple_unit *,
149 		    enum maple_dma_stat);
150 static void	maple_queue_retry(struct maple_softc *);
151 static void	maple_check_responses(struct maple_softc *);
152 static void	maple_event_thread(void *);
153 static int	maple_intr(void *);
154 static void	maple_callout(void *);
155 
156 int	maple_alloc_dma(size_t, vaddr_t *, paddr_t *);
157 #if 0
158 void	maple_free_dma(paddr_t, size_t);
159 #endif
160 
161 /*
162  * Global variables.
163  */
164 int	maple_polling;		/* Are we polling?  (Debugger mode) */
165 
166 CFATTACH_DECL(maple, sizeof(struct maple_softc),
167     maplematch, mapleattach, NULL, NULL);
168 
169 extern struct cfdriver maple_cd;
170 
171 dev_type_open(mapleopen);
172 dev_type_close(mapleclose);
173 dev_type_ioctl(mapleioctl);
174 
175 const struct cdevsw maple_cdevsw = {
176 	mapleopen, mapleclose, noread, nowrite, mapleioctl,
177 	nostop, notty, nopoll, nommap, nokqfilter,
178 };
179 
180 static int
181 maplematch(struct device *parent, struct cfdata *cf, void *aux)
182 {
183 
184 	return (1);
185 }
186 
187 static void
188 mapleattach(struct device *parent, struct device *self, void *aux)
189 {
190 	struct maple_softc *sc;
191 	struct maple_unit *u;
192 	vaddr_t dmabuffer;
193 	paddr_t dmabuffer_phys;
194 	u_int32_t *p;
195 	int port, subunit, f;
196 
197 	sc = (struct maple_softc *)self;
198 
199 	printf(": %s\n", sysasic_intr_string(IPL_MAPLE));
200 
201 	if (maple_alloc_dma(MAPLE_DMABUF_SIZE, &dmabuffer, &dmabuffer_phys)) {
202 		printf("%s: unable to allocate DMA buffers.\n",
203 		    sc->sc_dev.dv_xname);
204 		return;
205 	}
206 
207 	p = (u_int32_t *)dmabuffer;
208 
209 	for (port = 0; port < MAPLE_PORTS; port++)
210 		for (subunit = 0; subunit < MAPLE_SUBUNITS; subunit++) {
211 			u = &sc->sc_unit[port][subunit];
212 			u->port = port;
213 			u->subunit = subunit;
214 			u->u_dma_stat = MAPLE_DMA_IDLE;
215 			u->u_rxbuf = p;
216 			u->u_rxbuf_phys = SH3_P2SEG_TO_PHYS(p);
217 			p += 256;
218 
219 			for (f = 0; f < MAPLE_NFUNC; f++) {
220 				u->u_func[f].f_funcno = f;
221 				u->u_func[f].f_unit = u;
222 			}
223 		}
224 
225 	sc->sc_txbuf = p;
226 	sc->sc_txbuf_phys = SH3_P2SEG_TO_PHYS(p);
227 
228 	SIMPLEQ_INIT(&sc->sc_retryq);
229 	TAILQ_INIT(&sc->sc_probeq);
230 	TAILQ_INIT(&sc->sc_pingq);
231 	TAILQ_INIT(&sc->sc_periodicq);
232 	TAILQ_INIT(&sc->sc_periodicdeferq);
233 	TAILQ_INIT(&sc->sc_acmdq);
234 	TAILQ_INIT(&sc->sc_pcmdq);
235 
236 	MAPLE_RESET = RESET_MAGIC;
237 	MAPLE_RESET2 = 0;
238 
239 	MAPLE_SPEED = SPEED_2MBPS | TIMEOUT(50000);
240 
241 	MAPLE_ENABLE = 1;
242 
243 	maple_polling = 1;
244 	maple_scanbus(sc);
245 
246 	callout_init(&sc->maple_callout_ch);
247 
248 	sc->sc_intrhand = sysasic_intr_establish(SYSASIC_EVENT_MAPLE_DMADONE,
249 	    IPL_MAPLE, maple_intr, sc);
250 
251 	config_pending_incr();	/* create thread before mounting root */
252 	kthread_create(maple_create_event_thread, sc);
253 }
254 
255 static void
256 maple_create_event_thread(void *arg)
257 {
258 	struct maple_softc *sc = arg;
259 
260 	if (kthread_create1(maple_event_thread, sc, &sc->event_thread,
261 	    "%s", sc->sc_dev.dv_xname) == 0)
262 		return;
263 
264 	panic("%s: unable to create event thread", sc->sc_dev.dv_xname);
265 }
266 
267 /*
268  * initial device attach
269  */
270 static void
271 maple_scanbus(struct maple_softc *sc)
272 {
273 	struct maple_unit *u;
274 	int port;
275 	int last_port, last_subunit;
276 	int i;
277 
278 	KASSERT(cold && maple_polling);
279 
280 	/* probe all ports */
281 	for (port = 0; port < MAPLE_PORTS; port++) {
282 		u = &sc->sc_unit[port][0];
283 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
284 		{
285 			char buf[16];
286 			printf("%s: queued to probe 1\n",
287 			    maple_unit_name(buf, u->port, u->subunit));
288 		}
289 #endif
290 		TAILQ_INSERT_TAIL(&sc->sc_probeq, u, u_q);
291 		u->u_queuestat = MAPLE_QUEUE_PROBE;
292 	}
293 
294 	last_port = last_subunit = -1;
295 	maple_begin_txbuf(sc);
296 	while ((u = TAILQ_FIRST(&sc->sc_probeq)) != NULL) {
297 		/*
298 		 * Check wrap condition
299 		 */
300 		if (u->port < last_port || u->subunit <= last_subunit)
301 			break;
302 		last_port = u->port;
303 		if (u->port == MAPLE_PORTS - 1)
304 			last_subunit = u->subunit;
305 
306 		maple_unit_probe(sc);
307 		for (i = 10 /* just not forever */; maple_end_txbuf(sc); i--) {
308 			maple_start_poll(sc);
309 			maple_check_responses(sc);
310 			if (i == 0)
311 				break;
312 			/* attach may issue cmds */
313 			maple_queue_cmds(sc, &sc->sc_acmdq);
314 		}
315 	}
316 }
317 
318 void
319 maple_run_polling(struct device *dev)
320 {
321 	struct maple_softc *sc;
322 	int port, subunit;
323 	int i;
324 
325 	sc = (struct maple_softc *)dev;
326 
327 	/*
328 	 * first, make sure polling works
329 	 */
330 	while (MAPLE_STATE != 0)	/* XXX may lost a DMA cycle */
331 		;
332 
333 	/* XXX this will break internal state */
334 	for (port = 0; port < MAPLE_PORTS; port++)
335 		for (subunit = 0; subunit < MAPLE_SUBUNITS; subunit++)
336 			sc->sc_unit[port][subunit].u_dma_stat = MAPLE_DMA_IDLE;
337 	SIMPLEQ_INIT(&sc->sc_retryq);	/* XXX discard current retrys */
338 
339 	/*
340 	 * do polling (periodic status check only)
341 	 */
342 	maple_begin_txbuf(sc);
343 	maple_send_defered_periodic(sc);
344 	maple_send_periodic(sc);
345 	for (i = 10 /* just not forever */; maple_end_txbuf(sc); i--) {
346 		maple_start_poll(sc);
347 		maple_check_responses(sc);
348 		if (i == 0)
349 			break;
350 
351 		/* maple_check_responses() has executed maple_begin_txbuf() */
352 		maple_queue_retry(sc);
353 		maple_send_defered_periodic(sc);
354 	}
355 }
356 
357 static char *
358 maple_unit_name(char *buf, int port, int subunit)
359 {
360 
361 	sprintf(buf, "maple%c", port + 'A');
362 	if (subunit)
363 		sprintf(buf+6, "%d", subunit);
364 
365 	return buf;
366 }
367 
368 int
369 maple_alloc_dma(size_t size, vaddr_t *vap, paddr_t *pap)
370 {
371 	extern paddr_t avail_start, avail_end;	/* from pmap.c */
372 	struct pglist mlist;
373 	struct vm_page *m;
374 	int error;
375 
376 	size = round_page(size);
377 
378 	error = uvm_pglistalloc(size, avail_start, avail_end - PAGE_SIZE,
379 	    0, 0, &mlist, 1, 0);
380 	if (error)
381 		return (error);
382 
383 	m = TAILQ_FIRST(&mlist);
384 	*pap = VM_PAGE_TO_PHYS(m);
385 	*vap = SH3_PHYS_TO_P2SEG(VM_PAGE_TO_PHYS(m));
386 
387 	return (0);
388 }
389 
390 #if 0	/* currently unused */
391 void
392 maple_free_dma(paddr_t paddr, size_t size)
393 {
394 	struct pglist mlist;
395 	struct vm_page *m;
396 	bus_addr_t addr;
397 
398 	TAILQ_INIT(&mlist);
399 	for (addr = paddr; addr < paddr + size; addr += PAGE_SIZE) {
400 		m = PHYS_TO_VM_PAGE(addr);
401 		TAILQ_INSERT_TAIL(&mlist, m, pageq);
402 	}
403 	uvm_pglistfree(&mlist);
404 }
405 #endif
406 
407 static void
408 maple_begin_txbuf(struct maple_softc *sc)
409 {
410 
411 	sc->sc_txlink = sc->sc_txpos = sc->sc_txbuf;
412 	SIMPLEQ_INIT(&sc->sc_dmaq);
413 }
414 
415 static int
416 maple_end_txbuf(struct maple_softc *sc)
417 {
418 
419 	/* if no frame have been written, we can't mark the
420 	   list end, and so the DMA must not be activated   */
421 	if (sc->sc_txpos == sc->sc_txbuf)
422 		return (0);
423 
424 	*sc->sc_txlink |= 0x80000000;
425 
426 	return (1);
427 }
428 
429 static const int8_t subunit_code[] = { 0x20, 0x01, 0x02, 0x04, 0x08, 0x10 };
430 
431 static void
432 maple_queue_command(struct maple_softc *sc, struct maple_unit *u,
433 	int command, int datalen, const void *dataaddr)
434 {
435 	int to, from;
436 	u_int32_t *p = sc->sc_txpos;
437 
438 	/* Max data length = 255 longs = 1020 bytes */
439 	KASSERT(datalen >= 0 && datalen <= 255);
440 
441 	/* Compute sender and recipient address */
442 	from = u->port << 6;
443 	to = from | subunit_code[u->subunit];
444 
445 	sc->sc_txlink = p;
446 
447 	/* Set length of packet and destination port (A-D) */
448 	*p++ = datalen | (u->port << 16);
449 
450 	/* Write address to receive buffer where the response
451 	   frame should be put */
452 	*p++ = u->u_rxbuf_phys;
453 
454 	/* Create the frame header.  The fields are assembled "backwards"
455 	   because of the Maple Bus big-endianness.                       */
456 	*p++ = (command & 0xff) | (to << 8) | (from << 16) | (datalen << 24);
457 
458 	/* Copy parameter data, if any */
459 	if (datalen > 0) {
460 		const u_int32_t *param = dataaddr;
461 		int i;
462 		for (i = 0; i < datalen; i++)
463 			*p++ = *param++;
464 	}
465 
466 	sc->sc_txpos = p;
467 
468 	SIMPLEQ_INSERT_TAIL(&sc->sc_dmaq, u, u_dmaq);
469 }
470 
471 static void
472 maple_write_command(struct maple_softc *sc, struct maple_unit *u, int command,
473 	int datalen, const void *dataaddr)
474 {
475 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
476 	char buf[16];
477 
478 	if (u->u_retrycnt)
479 		printf("%s: retrycnt %d\n",
480 		    maple_unit_name(buf, u->port, u->subunit), u->u_retrycnt);
481 #endif
482 	u->u_retrycnt = 0;
483 	u->u_command = command;
484 	u->u_datalen = datalen;
485 	u->u_dataaddr = dataaddr;
486 
487 	maple_queue_command(sc, u, command, datalen, dataaddr);
488 }
489 
490 /* start DMA */
491 static void
492 maple_start(struct maple_softc *sc)
493 {
494 
495 	MAPLE_DMAADDR = sc->sc_txbuf_phys;
496 	MAPLE_STATE = 1;
497 }
498 
499 /* start DMA -- wait until DMA done */
500 static void
501 maple_start_poll(struct maple_softc *sc)
502 {
503 
504 	MAPLE_DMAADDR = sc->sc_txbuf_phys;
505 	MAPLE_STATE = 1;
506 	while (MAPLE_STATE != 0)
507 		;
508 }
509 
510 static void
511 maple_check_subunit_change(struct maple_softc *sc, struct maple_unit *u)
512 {
513 	struct maple_unit *u1;
514 	int port;
515 	int8_t unit_map;
516 	int units, un;
517 	int i;
518 
519 	KASSERT(u->subunit == 0);
520 
521 	port = u->port;
522 	unit_map = ((int8_t *) u->u_rxbuf)[2];
523 	if (sc->sc_port_unit_map[port] == unit_map)
524 		return;
525 
526 	units = ((unit_map & 0x1f) << 1) | 1;
527 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
528 	{
529 		char buf[16];
530 		printf("%s: unit_map 0x%x -> 0x%x (units 0x%x)\n",
531 		    maple_unit_name(buf, u->port, u->subunit),
532 		    sc->sc_port_unit_map[port], unit_map, units);
533 	}
534 #endif
535 #if 0	/* this detects unit removal rapidly but is not reliable */
536 	/* check for unit change */
537 	un = sc->sc_port_units[port] & ~units;
538 
539 	/* detach removed devices */
540 	for (i = MAPLE_SUBUNITS - 1; i > 0; i--)
541 		if (un & (1 << i))
542 			maple_detach_unit_nofix(sc, &sc->sc_unit[port][i]);
543 #endif
544 
545 	sc->sc_port_unit_map[port] = unit_map;
546 
547 	/* schedule scanning child devices */
548 	un = units & ~sc->sc_port_units[port];
549 	for (i = MAPLE_SUBUNITS - 1; i > 0; i--)
550 		if (un & (1 << i)) {
551 			u1 = &sc->sc_unit[port][i];
552 			maple_remove_from_queues(sc, u1);
553 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
554 			{
555 				char buf[16];
556 				printf("%s: queued to probe 2\n",
557 				    maple_unit_name(buf, u1->port, u1->subunit));
558 			}
559 #endif
560 			TAILQ_INSERT_HEAD(&sc->sc_probeq, u1, u_q);
561 			u1->u_queuestat = MAPLE_QUEUE_PROBE;
562 			u1->u_proberetry = 0;
563 		}
564 }
565 
566 static void
567 maple_check_unit_change(struct maple_softc *sc, struct maple_unit *u)
568 {
569 	struct maple_devinfo *newinfo = (void *) (u->u_rxbuf + 1);
570 	int port, subunit;
571 
572 	port = u->port;
573 	subunit = u->subunit;
574 	if (memcmp(&u->devinfo, newinfo, sizeof(struct maple_devinfo)) == 0)
575 		goto out;	/* no change */
576 
577 	/* unit inserted */
578 
579 	/* attach this device */
580 	u->devinfo = *newinfo;
581 	maple_attach_unit(sc, u);
582 
583 out:
584 	maple_remove_from_queues(sc, u);
585 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
586 	{
587 		char buf[16];
588 		printf("%s: queued to ping\n",
589 		    maple_unit_name(buf, u->port, u->subunit));
590 	}
591 #endif
592 	TAILQ_INSERT_TAIL(&sc->sc_pingq, u, u_q);
593 	u->u_queuestat = MAPLE_QUEUE_PING;
594 }
595 
596 static void
597 maple_print_unit(void *aux, const char *pnp)
598 {
599 	struct maple_attach_args *ma = aux;
600 	int port, subunit;
601 	char buf[16];
602 	char *prod, *p, oc;
603 
604 	port = ma->ma_unit->port;
605 	subunit = ma->ma_unit->subunit;
606 
607 	if (pnp != NULL)
608 		printf("%s at %s", maple_unit_name(buf, port, subunit), pnp);
609 
610 	printf(" port %d", port);
611 
612 	if (subunit != 0)
613 		printf(" subunit %d", subunit);
614 
615 #ifdef MAPLE_DEBUG
616 	printf(": a %#x c %#x fn %#x d %#x,%#x,%#x",
617 	    ma->ma_devinfo->di_area_code,
618 	    ma->ma_devinfo->di_connector_direction,
619 	    ntohl(ma->ma_devinfo->di_func),
620 	    ntohl(ma->ma_devinfo->di_function_data[0]),
621 	    ntohl(ma->ma_devinfo->di_function_data[1]),
622 	    ntohl(ma->ma_devinfo->di_function_data[2]));
623 #endif
624 
625 	/* nul termination */
626 	prod = ma->ma_devinfo->di_product_name;
627 	for (p = prod + sizeof ma->ma_devinfo->di_product_name; p >= prod; p--)
628 		if (p[-1] != '\0' && p[-1] != ' ')
629 			break;
630 	oc = *p;
631 	*p = '\0';
632 
633 	printf(": %s", prod);
634 
635 	*p = oc;	/* restore */
636 }
637 
638 static int
639 maplesubmatch(struct device *parent, struct cfdata *match, void *aux)
640 {
641 	struct maple_attach_args *ma = aux;
642 
643 	if (match->cf_loc[MAPLECF_PORT] != MAPLECF_PORT_DEFAULT &&
644 	    match->cf_loc[MAPLECF_PORT] != ma->ma_unit->port)
645 		return (0);
646 
647 	if (match->cf_loc[MAPLECF_SUBUNIT] != MAPLECF_SUBUNIT_DEFAULT &&
648 	    match->cf_loc[MAPLECF_SUBUNIT] != ma->ma_unit->subunit)
649 		return (0);
650 
651 	return (config_match(parent, match, aux));
652 }
653 
654 static int
655 mapleprint(void *aux, const char *str)
656 {
657 	struct maple_attach_args *ma = aux;
658 
659 #ifdef MAPLE_DEBUG
660 	if (str)
661 		aprint_normal("%s", str);
662 	aprint_normal(" function %d", ma->ma_function);
663 
664 	return UNCONF;
665 #else	/* quiet */
666 	if (!str)
667 		aprint_normal(" function %d", ma->ma_function);
668 
669 	return QUIET;
670 #endif
671 }
672 
673 static void
674 maple_attach_unit(struct maple_softc *sc, struct maple_unit *u)
675 {
676 	struct maple_attach_args ma;
677 	u_int32_t func;
678 	int f;
679 	char oldxname[16];
680 
681 	ma.ma_unit = u;
682 	ma.ma_devinfo = &u->devinfo;
683 	ma.ma_basedevinfo = &sc->sc_unit[u->port][0].devinfo;
684 	func = ntohl(ma.ma_devinfo->di_func);
685 
686 	maple_print_unit(&ma, sc->sc_dev.dv_xname);
687 	printf("\n");
688 	strcpy(oldxname, sc->sc_dev.dv_xname);
689 	maple_unit_name(sc->sc_dev.dv_xname, u->port, u->subunit);
690 
691 	for (f = 0; f < MAPLE_NFUNC; f++) {
692 		u->u_func[f].f_callback = NULL;
693 		u->u_func[f].f_arg = NULL;
694 		u->u_func[f].f_cmdstat = MAPLE_CMDSTAT_NONE;
695 		u->u_func[f].f_dev = NULL;
696 		if (func & MAPLE_FUNC(f)) {
697 			ma.ma_function = f;
698 			u->u_func[f].f_dev = config_found_sm(&sc->sc_dev, &ma,
699 			    mapleprint, maplesubmatch);
700 			u->u_ping_func = f;	/* XXX using largest func */
701 		}
702 	}
703 #ifdef MAPLE_MEMCARD_PING_HACK
704 	/*
705 	 * Some 3rd party memory card pretend to be Visual Memory,
706 	 * but need special handling for ping.
707 	 */
708 	if (func == (MAPLE_FUNC(MAPLE_FN_MEMCARD) | MAPLE_FUNC(MAPLE_FN_LCD) |
709 	    MAPLE_FUNC(MAPLE_FN_CLOCK))) {
710 		u->u_ping_func = MAPLE_FN_MEMCARD;
711 		u->u_ping_stat = MAPLE_PING_MEMCARD;
712 	} else {
713 		u->u_ping_stat = MAPLE_PING_NORMAL;
714 	}
715 #endif
716 	strcpy(sc->sc_dev.dv_xname, oldxname);
717 
718 	sc->sc_port_units[u->port] |= 1 << u->subunit;
719 }
720 
721 static void
722 maple_detach_unit_nofix(struct maple_softc *sc, struct maple_unit *u)
723 {
724 	struct maple_func *fn;
725 	struct device *dev;
726 	struct maple_unit *u1;
727 	int port;
728 	int error;
729 	int i;
730 	char buf[16];
731 
732 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
733 	printf("%s: remove\n", maple_unit_name(buf, u->port, u->subunit));
734 #endif
735 	maple_remove_from_queues(sc, u);
736 	port = u->port;
737 	sc->sc_port_units[port] &= ~(1 << u->subunit);
738 
739 	if (u->subunit == 0) {
740 		for (i = MAPLE_SUBUNITS - 1; i > 0; i--)
741 			maple_detach_unit_nofix(sc, &sc->sc_unit[port][i]);
742 	}
743 
744 	for (fn = u->u_func; fn < &u->u_func[MAPLE_NFUNC]; fn++) {
745 		if ((dev = fn->f_dev) != NULL) {
746 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
747 			printf("%s: detaching func %d\n",
748 			    maple_unit_name(buf, port, u->subunit),
749 			    fn->f_funcno);
750 #endif
751 
752 			/*
753 			 * Remove functions from command queue.
754 			 */
755 			switch (fn->f_cmdstat) {
756 			case MAPLE_CMDSTAT_ASYNC:
757 			case MAPLE_CMDSTAT_PERIODIC_DEFERED:
758 				TAILQ_REMOVE(&sc->sc_acmdq, fn, f_cmdq);
759 				break;
760 			case MAPLE_CMDSTAT_ASYNC_PERIODICQ:
761 			case MAPLE_CMDSTAT_PERIODIC:
762 				TAILQ_REMOVE(&sc->sc_pcmdq, fn, f_cmdq);
763 				break;
764 			default:
765 				break;
766 			}
767 
768 			/*
769 			 * Detach devices.
770 			 */
771 			if ((error = config_detach(fn->f_dev, DETACH_FORCE))) {
772 				printf("%s: failed to detach %s (func %d), errno %d\n",
773 				    maple_unit_name(buf, port, u->subunit),
774 				    fn->f_dev->dv_xname, fn->f_funcno, error);
775 			}
776 		}
777 
778 		maple_enable_periodic(&sc->sc_dev, u, fn->f_funcno, 0);
779 
780 		fn->f_dev = NULL;
781 		fn->f_callback = NULL;
782 		fn->f_arg = NULL;
783 		fn->f_cmdstat = MAPLE_CMDSTAT_NONE;
784 	}
785 	if (u->u_dma_stat == MAPLE_DMA_RETRY) {
786 		/* XXX expensive? */
787 		SIMPLEQ_FOREACH(u1, &sc->sc_retryq, u_dmaq) {
788 			if (u1 == u) {
789 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
790 				printf("%s: abort retry\n",
791 				    maple_unit_name(buf, port, u->subunit));
792 #endif
793 				SIMPLEQ_REMOVE(&sc->sc_retryq, u, maple_unit,
794 				    u_dmaq);
795 				break;
796 			}
797 		}
798 	}
799 	u->u_dma_stat = MAPLE_DMA_IDLE;
800 	u->u_noping = 0;
801 	/* u->u_dma_func = uninitialized; */
802 	KASSERT(u->getcond_func_set == 0);
803 	memset(&u->devinfo, 0, sizeof(struct maple_devinfo));
804 
805 	if (u->subunit == 0) {
806 		sc->sc_port_unit_map[port] = 0;
807 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
808 		{
809 			char buf[16];
810 			printf("%s: queued to probe 3\n",
811 			    maple_unit_name(buf, port, u->subunit));
812 		}
813 #endif
814 		TAILQ_INSERT_TAIL(&sc->sc_probeq, u, u_q);
815 		u->u_queuestat = MAPLE_QUEUE_PROBE;
816 	}
817 }
818 
819 static void
820 maple_detach_unit(struct maple_softc *sc, struct maple_unit *u)
821 {
822 
823 	maple_detach_unit_nofix(sc, u);
824 	if (u->subunit != 0)
825 		sc->sc_port_unit_map[u->port] &= ~(1 << (u->subunit - 1));
826 }
827 
828 /*
829  * Send a command (called by drivers)
830  *
831  * The "cataaddr" must not point at temporary storage like stack.
832  * Only one command (per function) is valid at a time.
833  */
834 void
835 maple_command(struct device *dev, struct maple_unit *u, int func,
836 	int command, int datalen, const void *dataaddr, int flags)
837 {
838 	struct maple_softc *sc = (void *) dev;
839 	struct maple_func *fn;
840 	int s;
841 
842 	KASSERT(func >= 0 && func < 32);
843 	KASSERT(command);
844 	KASSERT((flags & ~MAPLE_FLAG_CMD_PERIODIC_TIMING) == 0);
845 
846 	s = splsoftclock();
847 
848 	fn = &u->u_func[func];
849 #if 1 /*def DIAGNOSTIC*/
850 	{char buf[16];
851 	if (fn->f_cmdstat != MAPLE_CMDSTAT_NONE)
852 		panic("maple_command: %s func %d: requesting more than one commands",
853 		    maple_unit_name(buf, u->port, u->subunit), func);
854 	}
855 #endif
856 	fn->f_command = command;
857 	fn->f_datalen = datalen;
858 	fn->f_dataaddr = dataaddr;
859 	if (flags & MAPLE_FLAG_CMD_PERIODIC_TIMING) {
860 		fn->f_cmdstat = MAPLE_CMDSTAT_PERIODIC;
861 		TAILQ_INSERT_TAIL(&sc->sc_pcmdq, fn, f_cmdq);
862 	} else {
863 		fn->f_cmdstat = MAPLE_CMDSTAT_ASYNC;
864 		TAILQ_INSERT_TAIL(&sc->sc_acmdq, fn, f_cmdq);
865 		wakeup(&sc->sc_event);	/* wake for async event */
866 	}
867 	splx(s);
868 }
869 
870 static void
871 maple_queue_cmds(struct maple_softc *sc,
872 	struct maple_cmdq_head *head)
873 {
874 	struct maple_func *fn, *nextfn;
875 	struct maple_unit *u;
876 
877 	/*
878 	 * Note: since the queue element may be queued immediately,
879 	 *	 we can't use TAILQ_FOREACH.
880 	 */
881 	fn = TAILQ_FIRST(head);
882 	TAILQ_INIT(head);
883 	for ( ; fn; fn = nextfn) {
884 		nextfn = TAILQ_NEXT(fn, f_cmdq);
885 
886 		KASSERT(fn->f_cmdstat != MAPLE_CMDSTAT_NONE);
887 		u = fn->f_unit;
888 		if (u->u_dma_stat == MAPLE_DMA_IDLE) {
889 			maple_write_command(sc, u,
890 			    fn->f_command, fn->f_datalen, fn->f_dataaddr);
891 			u->u_dma_stat = (fn->f_cmdstat == MAPLE_CMDSTAT_ASYNC ||
892 			    fn->f_cmdstat == MAPLE_CMDSTAT_ASYNC_PERIODICQ) ?
893 			    MAPLE_DMA_ACMD : MAPLE_DMA_PCMD;
894 			u->u_dma_func = fn->f_funcno;
895 			fn->f_cmdstat = MAPLE_CMDSTAT_NONE;
896 		} else if (u->u_dma_stat == MAPLE_DMA_RETRY) {
897 			/* unit is busy --- try again */
898 			/*
899 			 * always add to periodic command queue
900 			 * (wait until the next periodic timing),
901 			 * since the unit will never be freed until the
902 			 * next periodic timing.
903 			 */
904 			switch (fn->f_cmdstat) {
905 			case MAPLE_CMDSTAT_ASYNC:
906 				fn->f_cmdstat = MAPLE_CMDSTAT_ASYNC_PERIODICQ;
907 				break;
908 			case MAPLE_CMDSTAT_PERIODIC_DEFERED:
909 				fn->f_cmdstat = MAPLE_CMDSTAT_PERIODIC;
910 				break;
911 			default:
912 				break;
913 			}
914 			TAILQ_INSERT_TAIL(&sc->sc_pcmdq, fn, f_cmdq);
915 		} else {
916 			/* unit is busy --- try again */
917 			/*
918 			 * always add to async command queue
919 			 * (process immediately)
920 			 */
921 			switch (fn->f_cmdstat) {
922 			case MAPLE_CMDSTAT_ASYNC_PERIODICQ:
923 				fn->f_cmdstat = MAPLE_CMDSTAT_ASYNC;
924 				break;
925 			case MAPLE_CMDSTAT_PERIODIC:
926 				fn->f_cmdstat = MAPLE_CMDSTAT_PERIODIC_DEFERED;
927 				break;
928 			default:
929 				break;
930 			}
931 			TAILQ_INSERT_TAIL(&sc->sc_acmdq, fn, f_cmdq);
932 		}
933 	}
934 }
935 
936 /* schedule probing a device */
937 static void
938 maple_unit_probe(struct maple_softc *sc)
939 {
940 	struct maple_unit *u;
941 
942 	if ((u = TAILQ_FIRST(&sc->sc_probeq)) != NULL) {
943 		KASSERT(u->u_dma_stat == MAPLE_DMA_IDLE);
944 		KASSERT(u->u_queuestat == MAPLE_QUEUE_PROBE);
945 		maple_remove_from_queues(sc, u);
946 		maple_write_command(sc, u, MAPLE_COMMAND_DEVINFO, 0, NULL);
947 		u->u_dma_stat = MAPLE_DMA_PROBE;
948 		/* u->u_dma_func = ignored; */
949 	}
950 }
951 
952 /*
953  * Enable/disable unit pinging (called by drivers)
954  */
955 /* ARGSUSED */
956 void
957 maple_enable_unit_ping(struct device *dev, struct maple_unit *u,
958 	int func, int enable)
959 {
960 #if 0	/* currently unused */
961 	struct maple_softc *sc = (void *) dev;
962 #endif
963 
964 	if (enable)
965 		u->u_noping &= ~MAPLE_FUNC(func);
966 	else
967 		u->u_noping |= MAPLE_FUNC(func);
968 }
969 
970 /* schedule pinging a device */
971 static void
972 maple_unit_ping(struct maple_softc *sc)
973 {
974 	struct maple_unit *u;
975 	struct maple_func *fn;
976 #ifdef MAPLE_MEMCARD_PING_HACK
977 	static const u_int32_t memcard_ping_arg[2] = {
978 		0x02000000,	/* htonl(MAPLE_FUNC(MAPLE_FN_MEMCARD)) */
979 		0		/* pt (1 byte) and unused 3 bytes */
980 	};
981 #endif
982 
983 	if ((u = TAILQ_FIRST(&sc->sc_pingq)) != NULL) {
984 		KASSERT(u->u_queuestat == MAPLE_QUEUE_PING);
985 		maple_remove_from_queues(sc, u);
986 		if (u->u_dma_stat == MAPLE_DMA_IDLE && u->u_noping == 0) {
987 #ifdef MAPLE_MEMCARD_PING_HACK
988 			if (u->u_ping_stat == MAPLE_PING_MINFO) {
989 				/* use MINFO for some memory cards */
990 				maple_write_command(sc, u,
991 				    MAPLE_COMMAND_GETMINFO,
992 				    2, memcard_ping_arg);
993 			} else
994 #endif
995 			{
996 				fn = &u->u_func[u->u_ping_func];
997 				fn->f_work = htonl(MAPLE_FUNC(u->u_ping_func));
998 				maple_write_command(sc, u,
999 				    MAPLE_COMMAND_GETCOND,
1000 				    1, &fn->f_work);
1001 			}
1002 			u->u_dma_stat = MAPLE_DMA_PING;
1003 			/* u->u_dma_func = XXX; */
1004 		} else {
1005 			/* no need if periodic */
1006 			TAILQ_INSERT_TAIL(&sc->sc_pingq, u, u_q);
1007 			u->u_queuestat = MAPLE_QUEUE_PING;
1008 		}
1009 	}
1010 }
1011 
1012 /*
1013  * Enable/disable periodic GETCOND (called by drivers)
1014  */
1015 void
1016 maple_enable_periodic(struct device *dev, struct maple_unit *u,
1017 	int func, int on)
1018 {
1019 	struct maple_softc *sc = (void *) dev;
1020 	struct maple_func *fn;
1021 
1022 	KASSERT(func >= 0 && func < 32);
1023 
1024 	fn = &u->u_func[func];
1025 
1026 	if (on) {
1027 		if (fn->f_periodic_stat == MAPLE_PERIODIC_NONE) {
1028 			TAILQ_INSERT_TAIL(&sc->sc_periodicq, fn, f_periodicq);
1029 			fn->f_periodic_stat = MAPLE_PERIODIC_INQ;
1030 			u->getcond_func_set |= MAPLE_FUNC(func);
1031 		}
1032 	} else {
1033 		if (fn->f_periodic_stat == MAPLE_PERIODIC_INQ)
1034 			TAILQ_REMOVE(&sc->sc_periodicq, fn, f_periodicq);
1035 		else if (fn->f_periodic_stat == MAPLE_PERIODIC_DEFERED)
1036 			TAILQ_REMOVE(&sc->sc_periodicdeferq, fn, f_periodicq);
1037 		fn->f_periodic_stat = MAPLE_PERIODIC_NONE;
1038 		u->getcond_func_set &= ~MAPLE_FUNC(func);
1039 	}
1040 }
1041 
1042 /*
1043  * queue periodic GETCOND
1044  */
1045 static int
1046 maple_send_defered_periodic(struct maple_softc *sc)
1047 {
1048 	struct maple_unit *u;
1049 	struct maple_func *fn, *nextfn;
1050 	int defer_remain = 0;
1051 
1052 	for (fn = TAILQ_FIRST(&sc->sc_periodicdeferq); fn; fn = nextfn) {
1053 		KASSERT(fn->f_periodic_stat == MAPLE_PERIODIC_DEFERED);
1054 
1055 		nextfn = TAILQ_NEXT(fn, f_periodicq);
1056 
1057 		u = fn->f_unit;
1058 		if (u->u_dma_stat == MAPLE_DMA_IDLE ||
1059 		    u->u_dma_stat == MAPLE_DMA_RETRY) {
1060 			/*
1061 			 * if IDLE  ->	queue this request
1062 			 * if RETRY ->	the unit never be freed until the next
1063 			 *		periodic timing, so just restore to
1064 			 *		the normal periodic queue.
1065 			 */
1066 			TAILQ_REMOVE(&sc->sc_periodicdeferq, fn, f_periodicq);
1067 			TAILQ_INSERT_TAIL(&sc->sc_periodicq, fn, f_periodicq);
1068 			fn->f_periodic_stat = MAPLE_PERIODIC_INQ;
1069 
1070 			if (u->u_dma_stat == MAPLE_DMA_IDLE) {
1071 				/*
1072 				 * queue periodic command
1073 				 */
1074 				fn->f_work = htonl(MAPLE_FUNC(fn->f_funcno));
1075 				maple_write_command(sc, u,
1076 				    MAPLE_COMMAND_GETCOND, 1, &fn->f_work);
1077 				u->u_dma_stat = MAPLE_DMA_PERIODIC;
1078 				u->u_dma_func = fn->f_funcno;
1079 			}
1080 		} else {
1081 			defer_remain = 1;
1082 		}
1083 	}
1084 
1085 	return defer_remain;
1086 }
1087 
1088 static void
1089 maple_send_periodic(struct maple_softc *sc)
1090 {
1091 	struct maple_unit *u;
1092 	struct maple_func *fn, *nextfn;
1093 
1094 	for (fn = TAILQ_FIRST(&sc->sc_periodicq); fn; fn = nextfn) {
1095 		KASSERT(fn->f_periodic_stat == MAPLE_PERIODIC_INQ);
1096 
1097 		nextfn = TAILQ_NEXT(fn, f_periodicq);
1098 
1099 		u = fn->f_unit;
1100 		if (u->u_dma_stat != MAPLE_DMA_IDLE) {
1101 			if (u->u_dma_stat != MAPLE_DMA_RETRY) {
1102 				/*
1103 				 * can't be queued --- move to defered queue
1104 				 */
1105 				TAILQ_REMOVE(&sc->sc_periodicq, fn,
1106 				    f_periodicq);
1107 				TAILQ_INSERT_TAIL(&sc->sc_periodicdeferq, fn,
1108 				    f_periodicq);
1109 				fn->f_periodic_stat = MAPLE_PERIODIC_DEFERED;
1110 			}
1111 		} else {
1112 			/*
1113 			 * queue periodic command
1114 			 */
1115 			fn->f_work = htonl(MAPLE_FUNC(fn->f_funcno));
1116 			maple_write_command(sc, u, MAPLE_COMMAND_GETCOND,
1117 			    1, &fn->f_work);
1118 			u->u_dma_stat = MAPLE_DMA_PERIODIC;
1119 			u->u_dma_func = fn->f_funcno;
1120 		}
1121 	}
1122 }
1123 
1124 static void
1125 maple_remove_from_queues(struct maple_softc *sc, struct maple_unit *u)
1126 {
1127 
1128 	/* remove from queues */
1129 	if (u->u_queuestat == MAPLE_QUEUE_PROBE)
1130 		TAILQ_REMOVE(&sc->sc_probeq, u, u_q);
1131 	else if (u->u_queuestat == MAPLE_QUEUE_PING)
1132 		TAILQ_REMOVE(&sc->sc_pingq, u, u_q);
1133 #ifdef DIAGNOSTIC
1134 	else if (u->u_queuestat != MAPLE_QUEUE_NONE)
1135 		panic("maple_remove_from_queues: queuestat %d", u->u_queuestat);
1136 #endif
1137 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
1138 	if (u->u_queuestat != MAPLE_QUEUE_NONE) {
1139 		char buf[16];
1140 		printf("%s: dequeued\n",
1141 		    maple_unit_name(buf, u->port, u->subunit));
1142 	}
1143 #endif
1144 
1145 	u->u_queuestat = MAPLE_QUEUE_NONE;
1146 }
1147 
1148 /*
1149  * retry current command at next periodic timing
1150  */
1151 static int
1152 maple_retry(struct maple_softc *sc, struct maple_unit *u,
1153 	enum maple_dma_stat st)
1154 {
1155 
1156 	KASSERT(st != MAPLE_DMA_IDLE && st != MAPLE_DMA_RETRY);
1157 
1158 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
1159 	if (u->u_retrycnt == 0) {
1160 		char buf[16];
1161 		printf("%s: retrying: %#x, %#x, %p\n",
1162 		    maple_unit_name(buf, u->port, u->subunit),
1163 		    u->u_command, u->u_datalen, u->u_dataaddr);
1164 	}
1165 #endif
1166 	if (u->u_retrycnt >= MAPLE_RETRY_MAX)
1167 		return 1;
1168 
1169 	u->u_retrycnt++;
1170 
1171 	u->u_saved_dma_stat = st;
1172 	u->u_dma_stat = MAPLE_DMA_RETRY; /* no new command before retry done */
1173 	SIMPLEQ_INSERT_TAIL(&sc->sc_retryq, u, u_dmaq);
1174 
1175 	return 0;
1176 }
1177 
1178 static void
1179 maple_queue_retry(struct maple_softc *sc)
1180 {
1181 	struct maple_unit *u, *nextu;
1182 
1183 	/*
1184 	 * Note: since the queue element is queued immediately
1185 	 *	 in maple_queue_command, we can't use SIMPLEQ_FOREACH.
1186 	 */
1187 	for (u = SIMPLEQ_FIRST(&sc->sc_retryq); u; u = nextu) {
1188 		nextu = SIMPLEQ_NEXT(u, u_dmaq);
1189 
1190 		/*
1191 		 * Retrying is in the highest priority, and the unit shall
1192 		 * always be free.
1193 		 */
1194 		KASSERT(u->u_dma_stat == MAPLE_DMA_RETRY);
1195 		maple_queue_command(sc, u, u->u_command, u->u_datalen,
1196 		    u->u_dataaddr);
1197 		u->u_dma_stat = u->u_saved_dma_stat;
1198 
1199 #ifdef DIAGNOSTIC
1200 		KASSERT(u->u_saved_dma_stat != MAPLE_DMA_IDLE);
1201 		u->u_saved_dma_stat = MAPLE_DMA_IDLE;
1202 #endif
1203 	}
1204 	SIMPLEQ_INIT(&sc->sc_retryq);
1205 }
1206 
1207 /*
1208  * Process DMA results.
1209  * Requires kernel context.
1210  */
1211 static void
1212 maple_check_responses(struct maple_softc *sc)
1213 {
1214 	struct maple_unit *u, *nextu;
1215 	struct maple_func *fn;
1216 	maple_response_t response;
1217 	int func_code, len;
1218 	int flags;
1219 	char buf[16];
1220 
1221 	/*
1222 	 * Note: since the queue element may be queued immediately,
1223 	 *	 we can't use SIMPLEQ_FOREACH.
1224 	 */
1225 	for (u = SIMPLEQ_FIRST(&sc->sc_dmaq), maple_begin_txbuf(sc);
1226 	    u; u = nextu) {
1227 		nextu = SIMPLEQ_NEXT(u, u_dmaq);
1228 
1229 		if (u->u_dma_stat == MAPLE_DMA_IDLE)
1230 			continue;	/* just detached or DDB was active */
1231 
1232 		/*
1233 		 * check for retransmission
1234 		 */
1235 		if ((response = u->u_rxbuf[0]) == MAPLE_RESPONSE_AGAIN) {
1236 			if (maple_retry(sc, u, u->u_dma_stat) == 0)
1237 				continue;
1238 			/* else pass error to upper layer */
1239 		}
1240 
1241 		len = (u->u_rxbuf[0] >> 24);	/* length in long */
1242 		len <<= 2;			/* length in byte */
1243 
1244 		/*
1245 		 * call handler
1246 		 */
1247 		if (u->u_dma_stat == MAPLE_DMA_PERIODIC) {
1248 			/*
1249 			 * periodic GETCOND
1250 			 */
1251 			u->u_dma_stat = MAPLE_DMA_IDLE;
1252 			func_code = u->u_dma_func;
1253 			if (response == MAPLE_RESPONSE_DATATRF && len > 0 &&
1254 			    ntohl(u->u_rxbuf[1]) == MAPLE_FUNC(func_code)) {
1255 				fn = &u->u_func[func_code];
1256 				if (fn->f_dev)
1257 					(*fn->f_callback)(fn->f_arg,
1258 					    (void *)u->u_rxbuf, len,
1259 					    MAPLE_FLAG_PERIODIC);
1260 			} else if (response == MAPLE_RESPONSE_NONE) {
1261 				/* XXX OK? */
1262 				/* detach */
1263 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
1264 				printf("%s: func: %d: periodic response %d\n",
1265 				    maple_unit_name(buf, u->port, u->subunit),
1266 				    u->u_dma_func,
1267 				    response);
1268 #endif
1269 				/*
1270 				 * Some 3rd party devices sometimes
1271 				 * do not respond.
1272 				 */
1273 				if (maple_retry(sc, u, MAPLE_DMA_PERIODIC))
1274 					maple_detach_unit(sc, u);
1275 			}
1276 			/* XXX check unexpected conditions? */
1277 
1278 		} else if (u->u_dma_stat == MAPLE_DMA_PROBE) {
1279 			KASSERT(u->u_queuestat == MAPLE_QUEUE_NONE);
1280 			u->u_dma_stat = MAPLE_DMA_IDLE;
1281 			switch (response) {
1282 			default:
1283 			case MAPLE_RESPONSE_NONE:
1284 				/*
1285 				 * Do not use maple_retry(), which conflicts
1286 				 * with probe structure.
1287 				 */
1288 				if (u->subunit != 0 &&
1289 				    ++u->u_proberetry > MAPLE_PROBERETRY_MAX) {
1290 					printf("%s: no response\n",
1291 					    maple_unit_name(buf,
1292 						u->port, u->subunit));
1293 				} else {
1294 					/* probe again */
1295 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 2
1296 					printf("%s: queued to probe 4\n",
1297 					    maple_unit_name(buf, u->port, u->subunit));
1298 #endif
1299 					TAILQ_INSERT_TAIL(&sc->sc_probeq, u,
1300 					    u_q);
1301 					u->u_queuestat = MAPLE_QUEUE_PROBE;
1302 				}
1303 				break;
1304 			case MAPLE_RESPONSE_DEVINFO:
1305 				/* check if the unit is changed */
1306 				maple_check_unit_change(sc, u);
1307 				break;
1308 			}
1309 
1310 		} else if (u->u_dma_stat == MAPLE_DMA_PING) {
1311 			KASSERT(u->u_queuestat == MAPLE_QUEUE_NONE);
1312 			u->u_dma_stat = MAPLE_DMA_IDLE;
1313 			switch (response) {
1314 			default:
1315 			case MAPLE_RESPONSE_NONE:
1316 				/*
1317 				 * Some 3rd party devices sometimes
1318 				 * do not respond.
1319 				 */
1320 				if (maple_retry(sc, u, MAPLE_DMA_PING)) {
1321 					/* detach */
1322 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
1323 					printf("%s: ping response %d\n",
1324 					    maple_unit_name(buf, u->port,
1325 						u->subunit),
1326 					    response);
1327 #endif
1328 #ifdef MAPLE_MEMCARD_PING_HACK
1329 					if (u->u_ping_stat
1330 					    == MAPLE_PING_MEMCARD) {
1331 						/*
1332 						 * The unit claims itself to be
1333 						 * a Visual Memory, and has
1334 						 * never responded to GETCOND.
1335 						 * Try again using MINFO, in
1336 						 * case it is a poorly
1337 						 * implemented 3rd party card.
1338 						 */
1339 #ifdef MAPLE_DEBUG
1340 						printf("%s: switching ping method\n",
1341 						    maple_unit_name(buf,
1342 							u->port, u->subunit));
1343 #endif
1344 						u->u_ping_stat
1345 						    = MAPLE_PING_MINFO;
1346 						TAILQ_INSERT_TAIL(&sc->sc_pingq,
1347 						    u, u_q);
1348 						u->u_queuestat
1349 						    = MAPLE_QUEUE_PING;
1350 					} else
1351 #endif	/* MAPLE_MEMCARD_PING_HACK */
1352 					maple_detach_unit(sc, u);
1353 				}
1354 				break;
1355 			case MAPLE_RESPONSE_BADCMD:
1356 			case MAPLE_RESPONSE_BADFUNC:
1357 			case MAPLE_RESPONSE_DATATRF:
1358 				TAILQ_INSERT_TAIL(&sc->sc_pingq, u, u_q);
1359 				u->u_queuestat = MAPLE_QUEUE_PING;
1360 #ifdef MAPLE_MEMCARD_PING_HACK
1361 				/*
1362 				 * If the unit responds to GETCOND, it is a
1363 				 * normal implementation.
1364 				 */
1365 				if (u->u_ping_stat == MAPLE_PING_MEMCARD)
1366 					u->u_ping_stat = MAPLE_PING_NORMAL;
1367 #endif
1368 				break;
1369 			}
1370 
1371 		} else {
1372 			/*
1373 			 * Note: Do not rely on the consistency of responses.
1374 			 */
1375 
1376 			if (response == MAPLE_RESPONSE_NONE) {
1377 				if (maple_retry(sc, u, u->u_dma_stat)) {
1378 					/* detach */
1379 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
1380 					printf("%s: command response %d\n",
1381 					    maple_unit_name(buf, u->port,
1382 						u->subunit),
1383 					    response);
1384 #endif
1385 					maple_detach_unit(sc, u);
1386 				}
1387 				continue;
1388 			}
1389 
1390 			flags = (u->u_dma_stat == MAPLE_DMA_PCMD) ?
1391 			    MAPLE_FLAG_CMD_PERIODIC_TIMING : 0;
1392 			u->u_dma_stat = MAPLE_DMA_IDLE;
1393 
1394 			func_code = u->u_dma_func;
1395 			fn = &u->u_func[func_code];
1396 			if (fn->f_dev == NULL) {
1397 				/* detached right now */
1398 #ifdef MAPLE_DEBUG
1399 				printf("%s: unknown function: function %d, response %d\n",
1400 				    maple_unit_name(buf, u->port, u->subunit),
1401 				    func_code, response);
1402 #endif
1403 				continue;
1404 			}
1405 			if (fn->f_callback != NULL) {
1406 				(*fn->f_callback)(fn->f_arg,
1407 				    (void *)u->u_rxbuf, len, flags);
1408 			}
1409 		}
1410 
1411 		/*
1412 		 * check for subunit change and schedule probing subunits
1413 		 */
1414 		if (u->subunit == 0 && response != MAPLE_RESPONSE_NONE &&
1415 		    response != MAPLE_RESPONSE_AGAIN &&
1416 		    ((int8_t *) u->u_rxbuf)[2] != sc->sc_port_unit_map[u->port])
1417 			maple_check_subunit_change(sc, u);
1418 	}
1419 }
1420 
1421 /*
1422  * Main Maple Bus thread
1423  */
1424 static void
1425 maple_event_thread(void *arg)
1426 {
1427 	struct maple_softc *sc = arg;
1428 	unsigned cnt = 1;	/* timing counter */
1429 	int s;
1430 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
1431 	int noreq = 0;
1432 #endif
1433 
1434 #ifdef MAPLE_DEBUG
1435 	printf("%s: forked event thread, pid %d\n",
1436 	    sc->sc_dev.dv_xname, sc->event_thread->p_pid);
1437 #endif
1438 
1439 	/* begin first DMA cycle */
1440 	maple_begin_txbuf(sc);
1441 
1442 	sc->sc_event = 1;
1443 
1444 	/* OK, continue booting system */
1445 	maple_polling = 0;
1446 	config_pending_decr();
1447 
1448 	for (;;) {
1449 		/*
1450 		 * queue requests
1451 		 */
1452 
1453 		/* queue async commands */
1454 		if (!TAILQ_EMPTY(&sc->sc_acmdq))
1455 			maple_queue_cmds(sc, &sc->sc_acmdq);
1456 
1457 		/* send defered periodic command */
1458 		if (!TAILQ_EMPTY(&sc->sc_periodicdeferq))
1459 			maple_send_defered_periodic(sc);
1460 
1461 		/* queue periodic commands */
1462 		if (sc->sc_event) {
1463 			/* queue commands on periodic timing */
1464 			if (!TAILQ_EMPTY(&sc->sc_pcmdq))
1465 				maple_queue_cmds(sc, &sc->sc_pcmdq);
1466 
1467 			/* retry */
1468 			if (!SIMPLEQ_EMPTY(&sc->sc_retryq))
1469 				maple_queue_retry(sc);
1470 
1471 			if ((cnt & 31) == 0)	/* XXX */
1472 				maple_unit_probe(sc);
1473 			cnt++;
1474 
1475 			maple_send_periodic(sc);
1476 			if ((cnt & 7) == 0)	/* XXX */
1477 				maple_unit_ping(sc);
1478 
1479 			/*
1480 			 * schedule periodic event
1481 			 */
1482 			sc->sc_event = 0;
1483 			callout_reset(&sc->maple_callout_ch,
1484 			    MAPLE_CALLOUT_TICKS, maple_callout, sc);
1485 		}
1486 
1487 		if (maple_end_txbuf(sc)) {
1488 
1489 			/*
1490 			 * start DMA
1491 			 */
1492 			s = splmaple();
1493 			maple_start(sc);
1494 
1495 			/*
1496 			 * wait until DMA done
1497 			 */
1498 			if (tsleep(&sc->sc_dmadone, PWAIT, "mdma", hz)
1499 			    == EWOULDBLOCK) {
1500 				/* was DDB active? */
1501 				printf("%s: timed out\n", sc->sc_dev.dv_xname);
1502 			}
1503 			splx(s);
1504 
1505 			/*
1506 			 * call handlers
1507 			 */
1508 			maple_check_responses(sc);
1509 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
1510 			noreq = 0;
1511 #endif
1512 		}
1513 #if defined(MAPLE_DEBUG) && MAPLE_DEBUG > 1
1514 		else {
1515 			/* weird if occurs in succession */
1516 #if MAPLE_DEBUG <= 2
1517 			if (noreq)	/* ignore first time */
1518 #endif
1519 				printf("%s: no request %d\n",
1520 				    sc->sc_dev.dv_xname, noreq);
1521 			noreq++;
1522 		}
1523 #endif
1524 
1525 		/*
1526 		 * wait for an event
1527 		 */
1528 		s = splsoftclock();
1529 		if (TAILQ_EMPTY(&sc->sc_acmdq) && sc->sc_event == 0 &&
1530 		    TAILQ_EMPTY(&sc->sc_periodicdeferq)) {
1531 			if (tsleep(&sc->sc_event, PWAIT, "mslp", hz)
1532 			    == EWOULDBLOCK) {
1533 				printf("%s: event timed out\n",
1534 				    sc->sc_dev.dv_xname);
1535 			}
1536 
1537 		}
1538 		splx(s);
1539 
1540 	}
1541 
1542 #if 0	/* maple root device can't be detached */
1543 	kthread_exit(0);
1544 	/* NOTREACHED */
1545 #endif
1546 }
1547 
1548 static int
1549 maple_intr(void *arg)
1550 {
1551 	struct maple_softc *sc = arg;
1552 
1553 	wakeup(&sc->sc_dmadone);
1554 
1555 	return 1;
1556 }
1557 
1558 static void
1559 maple_callout(void *ctx)
1560 {
1561 	struct maple_softc *sc = ctx;
1562 
1563 	sc->sc_event = 1;	/* mark as periodic event */
1564 	wakeup(&sc->sc_event);
1565 }
1566 
1567 /*
1568  * Install callback handler (called by drivers)
1569  */
1570 /* ARGSUSED */
1571 void
1572 maple_set_callback(struct device *dev, struct maple_unit *u, int func,
1573 	void (*callback)(void *, struct maple_response *, int, int), void *arg)
1574 {
1575 #if 0	/* currently unused */
1576 	struct maple_softc *sc = (void *) dev;
1577 #endif
1578 	struct maple_func *fn;
1579 
1580 	KASSERT(func >= 0 && func < MAPLE_NFUNC);
1581 
1582 	fn = &u->u_func[func];
1583 
1584 	fn->f_callback = callback;
1585 	fn->f_arg = arg;
1586 }
1587 
1588 /*
1589  * Return function definition data (called by drivers)
1590  */
1591 u_int32_t
1592 maple_get_function_data(struct maple_devinfo *devinfo, int function_code)
1593 {
1594 	int i, p = 0;
1595 	u_int32_t func;
1596 
1597 	func = ntohl(devinfo->di_func);
1598 	for (i = 31; i >= 0; --i)
1599 		if (func & MAPLE_FUNC(i)) {
1600 			if (function_code == i)
1601 				return ntohl(devinfo->di_function_data[p]);
1602 			else
1603 				if (++p >= 3)
1604 					break;
1605 		}
1606 
1607 	return (0);
1608 }
1609 
1610 /* Generic maple device interface */
1611 
1612 int
1613 mapleopen(dev_t dev, int flag, int mode, struct proc *p)
1614 {
1615 	struct maple_softc *sc;
1616 
1617 	sc = device_lookup(&maple_cd, MAPLEBUSUNIT(dev));
1618 	if (sc == NULL)			/* make sure it was attached */
1619 		return (ENXIO);
1620 
1621 	if (MAPLEPORT(dev) >= MAPLE_PORTS)
1622 		return (ENXIO);
1623 
1624 	if (MAPLESUBUNIT(dev) >= MAPLE_SUBUNITS)
1625 		return (ENXIO);
1626 
1627 	if (!(sc->sc_port_units[MAPLEPORT(dev)] & (1 << MAPLESUBUNIT(dev))))
1628 		return (ENXIO);
1629 
1630 	sc->sc_port_units_open[MAPLEPORT(dev)] |= 1 << MAPLESUBUNIT(dev);
1631 
1632 	return (0);
1633 }
1634 
1635 int
1636 mapleclose(dev_t dev, int flag, int mode, struct proc *p)
1637 {
1638 	struct maple_softc *sc;
1639 
1640 	sc = device_lookup(&maple_cd, MAPLEBUSUNIT(dev));
1641 
1642 	sc->sc_port_units_open[MAPLEPORT(dev)] &= ~(1 << MAPLESUBUNIT(dev));
1643 
1644 	return (0);
1645 }
1646 
1647 int
1648 maple_unit_ioctl(struct device *dev, struct maple_unit *u, u_long cmd,
1649     caddr_t data, int flag, struct proc *p)
1650 {
1651 	struct maple_softc *sc = (struct maple_softc *)dev;
1652 
1653 	if (!(sc->sc_port_units[u->port] & (1 << u->subunit)))
1654 		return (ENXIO);
1655 
1656 	switch(cmd) {
1657 	case MAPLEIO_GDEVINFO:
1658 		memcpy(data, &u->devinfo, sizeof(struct maple_devinfo));
1659 		break;
1660 	default:
1661 		return (EPASSTHROUGH);
1662 	}
1663 
1664 	return (0);
1665 }
1666 
1667 int
1668 mapleioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
1669 {
1670 	struct maple_softc *sc;
1671 	struct maple_unit *u;
1672 
1673 	sc = device_lookup(&maple_cd, MAPLEBUSUNIT(dev));
1674 	u = &sc->sc_unit[MAPLEPORT(dev)][MAPLESUBUNIT(dev)];
1675 
1676 	return (maple_unit_ioctl(&sc->sc_dev, u, cmd, data, flag, p));
1677 }
1678