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