1 /*	$OpenBSD: mpu401.c,v 1.9 2004/01/09 21:32:24 brad Exp $	*/
2 /*	$NetBSD: mpu401.c,v 1.3 1998/11/25 22:17:06 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (augustss@netbsd.org).
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
44 #include <sys/syslog.h>
45 #include <sys/device.h>
46 #include <sys/proc.h>
47 #include <sys/buf.h>
48 
49 #include <machine/cpu.h>
50 #include <machine/intr.h>
51 #include <machine/bus.h>
52 
53 #include <dev/midi_if.h>
54 
55 #include <dev/isa/isavar.h>
56 #include <dev/isa/isadmavar.h>
57 
58 #include <dev/ic/mpuvar.h>
59 
60 #ifndef splaudio
61 #define splaudio() splbio()	/* XXX found in audio_if.h normally */
62 #endif
63 
64 #ifdef AUDIO_DEBUG
65 #define DPRINTF(x)	if (mpu401debug) printf x
66 #define DPRINTFN(n,x)	if (mpu401debug >= (n)) printf x
67 int	mpu401debug = 0;
68 #else
69 #define DPRINTF(x)
70 #define DPRINTFN(n,x)
71 #endif
72 
73 #define MPU_GETSTATUS(iot, ioh) (bus_space_read_1(iot, ioh, MPU_STATUS))
74 
75 int	mpu_reset(struct mpu_softc *);
76 static	__inline int mpu_waitready(struct mpu_softc *);
77 void	mpu_readinput(struct mpu_softc *);
78 
79 struct cfdriver mpu_cd = {
80 	NULL, "mpu", DV_DULL
81 };
82 
83 struct midi_hw_if mpu_midi_hw_if = {
84 	mpu_open,
85 	mpu_close,
86 	mpu_output,
87 	mpu_getinfo,
88 	0,                      /* ioctl */
89 };
90 
91 int
mpu_find(v)92 mpu_find(v)
93 	void *v;
94 {
95 	struct mpu_softc *sc = v;
96 
97 	if (MPU_GETSTATUS(sc->iot, sc->ioh) == 0xff) {
98 		DPRINTF(("mpu_find: No status\n"));
99 		goto bad;
100 	}
101 	sc->open = 0;
102 	sc->intr = 0;
103 	if (mpu_reset(sc) == 0)
104 		return 1;
105 bad:
106 	return 0;
107 }
108 
109 static __inline int
mpu_waitready(sc)110 mpu_waitready(sc)
111 	struct mpu_softc *sc;
112 {
113 	int i;
114 
115 	for(i = 0; i < MPU_MAXWAIT; i++) {
116 		if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY))
117 			return 0;
118 		delay(10);
119 	}
120 	return 1;
121 }
122 
123 int
mpu_reset(sc)124 mpu_reset(sc)
125 	struct mpu_softc *sc;
126 {
127 	bus_space_tag_t iot = sc->iot;
128 	bus_space_handle_t ioh = sc->ioh;
129 	int i;
130 	int s;
131 
132 	if (mpu_waitready(sc)) {
133 		DPRINTF(("mpu_reset: not ready\n"));
134 		return EIO;
135 	}
136 	s = splaudio();		/* Don't let the interrupt get our ACK. */
137 	bus_space_write_1(iot, ioh, MPU_COMMAND, MPU_RESET);
138 	for(i = 0; i < 2*MPU_MAXWAIT; i++) {
139 		if (!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY) &&
140 		    bus_space_read_1(iot, ioh, MPU_DATA) == MPU_ACK) {
141 			splx(s);
142 			return 0;
143 		}
144 	}
145 	splx(s);
146 	DPRINTF(("mpu_reset: No ACK\n"));
147 	return EIO;
148 }
149 
150 int
mpu_open(v,flags,iintr,ointr,arg)151 mpu_open(v, flags, iintr, ointr, arg)
152 	void *v;
153 	int flags;
154 	void (*iintr)(void *, int);
155 	void (*ointr)(void *);
156 	void *arg;
157 {
158 	struct mpu_softc *sc = v;
159 
160         DPRINTF(("mpu_open: sc=%p\n", sc));
161 
162 	if (sc->open)
163 		return EBUSY;
164 	if (mpu_reset(sc) != 0)
165 		return EIO;
166 
167 	bus_space_write_1(sc->iot, sc->ioh, MPU_COMMAND, MPU_UART_MODE);
168 	sc->open = 1;
169 	sc->intr = iintr;
170 	sc->arg = arg;
171 	return 0;
172 }
173 
174 void
mpu_close(v)175 mpu_close(v)
176 	void *v;
177 {
178 	struct mpu_softc *sc = v;
179 
180         DPRINTF(("mpu_close: sc=%p\n", sc));
181 
182 	sc->open = 0;
183 	sc->intr = 0;
184 	mpu_reset(sc); /* exit UART mode */
185 }
186 
187 void
mpu_readinput(sc)188 mpu_readinput(sc)
189 	struct mpu_softc *sc;
190 {
191 	bus_space_tag_t iot = sc->iot;
192 	bus_space_handle_t ioh = sc->ioh;
193 	int data;
194 
195 	while(!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY)) {
196 		data = bus_space_read_1(iot, ioh, MPU_DATA);
197 		DPRINTFN(3, ("mpu_rea: sc=%p 0x%02x\n", sc, data));
198 		if (sc->intr)
199 			sc->intr(sc->arg, data);
200 	}
201 }
202 
203 int
mpu_output(v,d)204 mpu_output(v, d)
205 	void *v;
206 	int d;
207 {
208 	struct mpu_softc *sc = v;
209 	int s;
210 
211 	DPRINTFN(3, ("mpu_output: sc=%p 0x%02x\n", sc, d));
212 	if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY)) {
213 		s = splaudio();
214 		mpu_readinput(sc);
215 		splx(s);
216 	}
217 	if (mpu_waitready(sc)) {
218 		DPRINTF(("mpu_output: not ready\n"));
219 		return EIO;
220 	}
221 	bus_space_write_1(sc->iot, sc->ioh, MPU_DATA, d);
222 	return 0;
223 }
224 
225 void
mpu_getinfo(addr,mi)226 mpu_getinfo(addr, mi)
227 	void *addr;
228 	struct midi_info *mi;
229 {
230 	mi->name = "MPU-401 MIDI UART";
231 	mi->props = 0;
232 }
233 
234 int
mpu_intr(v)235 mpu_intr(v)
236 	void *v;
237 {
238 	struct mpu_softc *sc = v;
239 
240 	if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY) {
241 		DPRINTF(("mpu_intr: no data\n"));
242 		return 0;
243 	}
244 	mpu_readinput(sc);
245 	return 1;
246 }
247