1 /* $OpenBSD: mpu401.c,v 1.16 2021/03/07 06:17:04 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(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 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 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 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 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 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 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 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 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