xref: /openbsd-src/sys/dev/isa/mpu401.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: mpu401.c,v 1.11 2008/06/26 05:42:16 ray 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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/syslog.h>
38 #include <sys/device.h>
39 #include <sys/proc.h>
40 #include <sys/buf.h>
41 
42 #include <machine/cpu.h>
43 #include <machine/intr.h>
44 #include <machine/bus.h>
45 
46 #include <dev/midi_if.h>
47 
48 #include <dev/isa/isavar.h>
49 #include <dev/isa/isadmavar.h>
50 
51 #include <dev/ic/mpuvar.h>
52 
53 #ifndef splaudio
54 #define splaudio() splbio()	/* XXX found in audio_if.h normally */
55 #endif
56 
57 #ifdef AUDIO_DEBUG
58 #define DPRINTF(x)	if (mpu401debug) printf x
59 #define DPRINTFN(n,x)	if (mpu401debug >= (n)) printf x
60 int	mpu401debug = 0;
61 #else
62 #define DPRINTF(x)
63 #define DPRINTFN(n,x)
64 #endif
65 
66 #define MPU_GETSTATUS(iot, ioh) (bus_space_read_1(iot, ioh, MPU_STATUS))
67 
68 int	mpu_reset(struct mpu_softc *);
69 static	__inline int mpu_waitready(struct mpu_softc *);
70 void	mpu_readinput(struct mpu_softc *);
71 
72 struct cfdriver mpu_cd = {
73 	NULL, "mpu", DV_DULL
74 };
75 
76 struct midi_hw_if mpu_midi_hw_if = {
77 	mpu_open,
78 	mpu_close,
79 	mpu_output,
80 	0,			/* flush */
81 	mpu_getinfo,
82 	0,                      /* ioctl */
83 };
84 
85 int
86 mpu_find(v)
87 	void *v;
88 {
89 	struct mpu_softc *sc = v;
90 
91 	if (MPU_GETSTATUS(sc->iot, sc->ioh) == 0xff) {
92 		DPRINTF(("mpu_find: No status\n"));
93 		goto bad;
94 	}
95 	sc->open = 0;
96 	sc->intr = 0;
97 	if (mpu_reset(sc) == 0)
98 		return 1;
99 bad:
100 	return 0;
101 }
102 
103 static __inline int
104 mpu_waitready(sc)
105 	struct mpu_softc *sc;
106 {
107 	int i;
108 
109 	for(i = 0; i < MPU_MAXWAIT; i++) {
110 		if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY))
111 			return 0;
112 		delay(10);
113 	}
114 	return 1;
115 }
116 
117 int
118 mpu_reset(sc)
119 	struct mpu_softc *sc;
120 {
121 	bus_space_tag_t iot = sc->iot;
122 	bus_space_handle_t ioh = sc->ioh;
123 	int i;
124 	int s;
125 
126 	if (mpu_waitready(sc)) {
127 		DPRINTF(("mpu_reset: not ready\n"));
128 		return EIO;
129 	}
130 	s = splaudio();		/* Don't let the interrupt get our ACK. */
131 	bus_space_write_1(iot, ioh, MPU_COMMAND, MPU_RESET);
132 	for(i = 0; i < 2*MPU_MAXWAIT; i++) {
133 		if (!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY) &&
134 		    bus_space_read_1(iot, ioh, MPU_DATA) == MPU_ACK) {
135 			splx(s);
136 			return 0;
137 		}
138 	}
139 	splx(s);
140 	DPRINTF(("mpu_reset: No ACK\n"));
141 	return EIO;
142 }
143 
144 int
145 mpu_open(v, flags, iintr, ointr, arg)
146 	void *v;
147 	int flags;
148 	void (*iintr)(void *, int);
149 	void (*ointr)(void *);
150 	void *arg;
151 {
152 	struct mpu_softc *sc = v;
153 
154         DPRINTF(("mpu_open: sc=%p\n", sc));
155 
156 	if (sc->open)
157 		return EBUSY;
158 	if (mpu_reset(sc) != 0)
159 		return EIO;
160 
161 	bus_space_write_1(sc->iot, sc->ioh, MPU_COMMAND, MPU_UART_MODE);
162 	sc->open = 1;
163 	sc->intr = iintr;
164 	sc->arg = arg;
165 	return 0;
166 }
167 
168 void
169 mpu_close(v)
170 	void *v;
171 {
172 	struct mpu_softc *sc = v;
173 
174         DPRINTF(("mpu_close: sc=%p\n", sc));
175 
176 	sc->open = 0;
177 	sc->intr = 0;
178 	mpu_reset(sc); /* exit UART mode */
179 }
180 
181 void
182 mpu_readinput(sc)
183 	struct mpu_softc *sc;
184 {
185 	bus_space_tag_t iot = sc->iot;
186 	bus_space_handle_t ioh = sc->ioh;
187 	int data;
188 
189 	while(!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY)) {
190 		data = bus_space_read_1(iot, ioh, MPU_DATA);
191 		DPRINTFN(3, ("mpu_rea: sc=%p 0x%02x\n", sc, data));
192 		if (sc->intr)
193 			sc->intr(sc->arg, data);
194 	}
195 }
196 
197 int
198 mpu_output(v, d)
199 	void *v;
200 	int d;
201 {
202 	struct mpu_softc *sc = v;
203 	int s;
204 
205 	DPRINTFN(3, ("mpu_output: sc=%p 0x%02x\n", sc, d));
206 	if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY)) {
207 		s = splaudio();
208 		mpu_readinput(sc);
209 		splx(s);
210 	}
211 	if (mpu_waitready(sc)) {
212 		DPRINTF(("mpu_output: not ready\n"));
213 		return EIO;
214 	}
215 	bus_space_write_1(sc->iot, sc->ioh, MPU_DATA, d);
216 	return 0;
217 }
218 
219 void
220 mpu_getinfo(addr, mi)
221 	void *addr;
222 	struct midi_info *mi;
223 {
224 	mi->name = "MPU-401 MIDI UART";
225 	mi->props = 0;
226 }
227 
228 int
229 mpu_intr(v)
230 	void *v;
231 {
232 	struct mpu_softc *sc = v;
233 
234 	if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY) {
235 		DPRINTF(("mpu_intr: no data\n"));
236 		return 0;
237 	}
238 	mpu_readinput(sc);
239 	return 1;
240 }
241