xref: /openbsd-src/sys/dev/isa/mpu401.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: mpu401.c,v 1.15 2015/03/14 03:38:47 jsg 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/buf.h>
40 
41 #include <machine/cpu.h>
42 #include <machine/intr.h>
43 #include <machine/bus.h>
44 
45 #include <dev/audio_if.h>
46 #include <dev/midi_if.h>
47 
48 #include <dev/isa/isavar.h>
49 
50 #include <dev/ic/mpuvar.h>
51 
52 #ifdef AUDIO_DEBUG
53 #define DPRINTF(x)	if (mpu401debug) printf x
54 #define DPRINTFN(n,x)	if (mpu401debug >= (n)) printf x
55 int	mpu401debug = 0;
56 #else
57 #define DPRINTF(x)
58 #define DPRINTFN(n,x)
59 #endif
60 
61 #define MPU_GETSTATUS(iot, ioh) (bus_space_read_1(iot, ioh, MPU_STATUS))
62 
63 int	mpu_reset(struct mpu_softc *);
64 static	__inline int mpu_waitready(struct mpu_softc *);
65 void	mpu_readinput(struct mpu_softc *);
66 
67 struct cfdriver mpu_cd = {
68 	NULL, "mpu", DV_DULL
69 };
70 
71 struct midi_hw_if mpu_midi_hw_if = {
72 	mpu_open,
73 	mpu_close,
74 	mpu_output,
75 	0,			/* flush */
76 	mpu_getinfo,
77 	0,                      /* ioctl */
78 };
79 
80 int
81 mpu_find(v)
82 	void *v;
83 {
84 	struct mpu_softc *sc = v;
85 
86 	if (MPU_GETSTATUS(sc->iot, sc->ioh) == 0xff) {
87 		DPRINTF(("mpu_find: No status\n"));
88 		goto bad;
89 	}
90 	sc->open = 0;
91 	sc->intr = 0;
92 	if (mpu_reset(sc) == 0)
93 		return 1;
94 bad:
95 	return 0;
96 }
97 
98 static __inline int
99 mpu_waitready(sc)
100 	struct mpu_softc *sc;
101 {
102 	int i;
103 
104 	for(i = 0; i < MPU_MAXWAIT; i++) {
105 		if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY))
106 			return 0;
107 		delay(10);
108 	}
109 	return 1;
110 }
111 
112 int
113 mpu_reset(sc)
114 	struct mpu_softc *sc;
115 {
116 	bus_space_tag_t iot = sc->iot;
117 	bus_space_handle_t ioh = sc->ioh;
118 	int i;
119 
120 	if (mpu_waitready(sc)) {
121 		DPRINTF(("mpu_reset: not ready\n"));
122 		return EIO;
123 	}
124 	mtx_enter(&audio_lock);	/* Don't let the interrupt get our ACK. */
125 	bus_space_write_1(iot, ioh, MPU_COMMAND, MPU_RESET);
126 	for(i = 0; i < 2*MPU_MAXWAIT; i++) {
127 		if (!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY) &&
128 		    bus_space_read_1(iot, ioh, MPU_DATA) == MPU_ACK) {
129 			mtx_leave(&audio_lock);
130 			return 0;
131 		}
132 	}
133 	mtx_leave(&audio_lock);
134 	DPRINTF(("mpu_reset: No ACK\n"));
135 	return EIO;
136 }
137 
138 int
139 mpu_open(v, flags, iintr, ointr, arg)
140 	void *v;
141 	int flags;
142 	void (*iintr)(void *, int);
143 	void (*ointr)(void *);
144 	void *arg;
145 {
146 	struct mpu_softc *sc = v;
147 
148         DPRINTF(("mpu_open: sc=%p\n", sc));
149 
150 	if (sc->open)
151 		return EBUSY;
152 	if (mpu_reset(sc) != 0)
153 		return EIO;
154 
155 	bus_space_write_1(sc->iot, sc->ioh, MPU_COMMAND, MPU_UART_MODE);
156 	sc->open = 1;
157 	sc->intr = iintr;
158 	sc->arg = arg;
159 	return 0;
160 }
161 
162 void
163 mpu_close(v)
164 	void *v;
165 {
166 	struct mpu_softc *sc = v;
167 
168         DPRINTF(("mpu_close: sc=%p\n", sc));
169 
170 	sc->open = 0;
171 	sc->intr = 0;
172 	mpu_reset(sc); /* exit UART mode */
173 }
174 
175 void
176 mpu_readinput(sc)
177 	struct mpu_softc *sc;
178 {
179 	bus_space_tag_t iot = sc->iot;
180 	bus_space_handle_t ioh = sc->ioh;
181 	int data;
182 
183 	while(!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY)) {
184 		data = bus_space_read_1(iot, ioh, MPU_DATA);
185 		DPRINTFN(3, ("mpu_rea: sc=%p 0x%02x\n", sc, data));
186 		if (sc->intr)
187 			sc->intr(sc->arg, data);
188 	}
189 }
190 
191 /*
192  * called with audio_lock
193  */
194 int
195 mpu_output(v, d)
196 	void *v;
197 	int d;
198 {
199 	struct mpu_softc *sc = v;
200 
201 	DPRINTFN(3, ("mpu_output: sc=%p 0x%02x\n", sc, d));
202 	if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY)) {
203 		mpu_readinput(sc);
204 	}
205 	if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY)
206 		delay(10);
207 	if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY)
208 		return 0;
209 	bus_space_write_1(sc->iot, sc->ioh, MPU_DATA, d);
210 	return 1;
211 }
212 
213 void
214 mpu_getinfo(addr, mi)
215 	void *addr;
216 	struct midi_info *mi;
217 {
218 	mi->name = "MPU-401 MIDI UART";
219 	mi->props = 0;
220 }
221 
222 int
223 mpu_intr(v)
224 	void *v;
225 {
226 	struct mpu_softc *sc = v;
227 
228 	mtx_enter(&audio_lock);
229 	if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY) {
230 		mtx_leave(&audio_lock);
231 		DPRINTF(("mpu_intr: no data\n"));
232 		return 0;
233 	}
234 	mpu_readinput(sc);
235 	mtx_leave(&audio_lock);
236 	return 1;
237 }
238