1 /* $OpenBSD: mpu401.c,v 1.17 2022/03/21 19:22:40 miod 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 const 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
mpu_find(void * v)81 mpu_find(void *v)
82 {
83 struct mpu_softc *sc = v;
84
85 if (MPU_GETSTATUS(sc->iot, sc->ioh) == 0xff) {
86 DPRINTF(("mpu_find: No status\n"));
87 goto bad;
88 }
89 sc->open = 0;
90 sc->intr = 0;
91 if (mpu_reset(sc) == 0)
92 return 1;
93 bad:
94 return 0;
95 }
96
97 static __inline int
mpu_waitready(struct mpu_softc * sc)98 mpu_waitready(struct mpu_softc *sc)
99 {
100 int i;
101
102 for(i = 0; i < MPU_MAXWAIT; i++) {
103 if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY))
104 return 0;
105 delay(10);
106 }
107 return 1;
108 }
109
110 int
mpu_reset(struct mpu_softc * sc)111 mpu_reset(struct mpu_softc *sc)
112 {
113 bus_space_tag_t iot = sc->iot;
114 bus_space_handle_t ioh = sc->ioh;
115 int i;
116
117 if (mpu_waitready(sc)) {
118 DPRINTF(("mpu_reset: not ready\n"));
119 return EIO;
120 }
121 mtx_enter(&audio_lock); /* Don't let the interrupt get our ACK. */
122 bus_space_write_1(iot, ioh, MPU_COMMAND, MPU_RESET);
123 for(i = 0; i < 2*MPU_MAXWAIT; i++) {
124 if (!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY) &&
125 bus_space_read_1(iot, ioh, MPU_DATA) == MPU_ACK) {
126 mtx_leave(&audio_lock);
127 return 0;
128 }
129 }
130 mtx_leave(&audio_lock);
131 DPRINTF(("mpu_reset: No ACK\n"));
132 return EIO;
133 }
134
135 int
mpu_open(void * v,int flags,void (* iintr)(void *,int),void (* ointr)(void *),void * arg)136 mpu_open(void *v, int flags, void (*iintr)(void *, int), void (*ointr)(void *),
137 void *arg)
138 {
139 struct mpu_softc *sc = v;
140
141 DPRINTF(("mpu_open: sc=%p\n", sc));
142
143 if (sc->open)
144 return EBUSY;
145 if (mpu_reset(sc) != 0)
146 return EIO;
147
148 bus_space_write_1(sc->iot, sc->ioh, MPU_COMMAND, MPU_UART_MODE);
149 sc->open = 1;
150 sc->intr = iintr;
151 sc->arg = arg;
152 return 0;
153 }
154
155 void
mpu_close(void * v)156 mpu_close(void *v)
157 {
158 struct mpu_softc *sc = v;
159
160 DPRINTF(("mpu_close: sc=%p\n", sc));
161
162 sc->open = 0;
163 sc->intr = 0;
164 mpu_reset(sc); /* exit UART mode */
165 }
166
167 void
mpu_readinput(struct mpu_softc * sc)168 mpu_readinput(struct mpu_softc *sc)
169 {
170 bus_space_tag_t iot = sc->iot;
171 bus_space_handle_t ioh = sc->ioh;
172 int data;
173
174 while(!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY)) {
175 data = bus_space_read_1(iot, ioh, MPU_DATA);
176 DPRINTFN(3, ("mpu_rea: sc=%p 0x%02x\n", sc, data));
177 if (sc->intr)
178 sc->intr(sc->arg, data);
179 }
180 }
181
182 /*
183 * called with audio_lock
184 */
185 int
mpu_output(void * v,int d)186 mpu_output(void *v, int d)
187 {
188 struct mpu_softc *sc = v;
189
190 DPRINTFN(3, ("mpu_output: sc=%p 0x%02x\n", sc, d));
191 if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY)) {
192 mpu_readinput(sc);
193 }
194 if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY)
195 delay(10);
196 if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY)
197 return 0;
198 bus_space_write_1(sc->iot, sc->ioh, MPU_DATA, d);
199 return 1;
200 }
201
202 void
mpu_getinfo(void * addr,struct midi_info * mi)203 mpu_getinfo(void *addr, struct midi_info *mi)
204 {
205 mi->name = "MPU-401 MIDI UART";
206 mi->props = 0;
207 }
208
209 int
mpu_intr(void * v)210 mpu_intr(void *v)
211 {
212 struct mpu_softc *sc = v;
213
214 mtx_enter(&audio_lock);
215 if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY) {
216 mtx_leave(&audio_lock);
217 DPRINTF(("mpu_intr: no data\n"));
218 return 0;
219 }
220 mpu_readinput(sc);
221 mtx_leave(&audio_lock);
222 return 1;
223 }
224