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