1 /* $NetBSD: mpu.c,v 1.19 2012/01/21 16:49:26 chs Exp $ */ 2 3 /* 4 * Copyright (c) 1998, 2008 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) and by Andrew Doran. 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.19 2012/01/21 16:49:26 chs 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 static void mpu_get_locks(void *, kmutex_t **, kmutex_t **); 85 86 const struct midi_hw_if mpu_midi_hw_if = { 87 mpu_open, 88 mpu_close, 89 mpu_output, 90 mpu_getinfo, 91 0, /* ioctl */ 92 mpu_get_locks, 93 }; 94 95 int 96 mpu_find(struct mpu_softc *sc) 97 { 98 if (MPU_GETSTATUS(sc->iot, sc->ioh) == 0xff) { 99 DPRINTF(("%s: No status\n", __func__)); 100 goto bad; 101 } 102 sc->open = 0; 103 sc->intr = 0; 104 if (mpu_reset(sc) == 0) 105 return 1; 106 bad: 107 return 0; 108 } 109 110 void 111 mpu_attach(struct mpu_softc *sc) 112 { 113 114 if (sc->lock == NULL) { 115 panic("mpu_attach: no lock"); 116 } 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 KASSERT(sc->lock == NULL || mutex_owned(sc->lock)); 127 128 for (i = 0; i < MPU_MAXWAIT; i++) { 129 if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_OUTPUT_BUSY)) 130 return 0; 131 delay(10); 132 } 133 return 1; 134 } 135 136 static int 137 mpu_reset(struct mpu_softc *sc) 138 { 139 bus_space_tag_t iot = sc->iot; 140 bus_space_handle_t ioh = sc->ioh; 141 int i; 142 143 KASSERT(sc->lock == NULL || mutex_owned(sc->lock)); 144 145 if (mpu_waitready(sc)) { 146 DPRINTF(("%s: not ready\n", __func__)); 147 return EIO; 148 } 149 bus_space_write_1(iot, ioh, MPU_COMMAND, MPU_RESET); 150 for (i = 0; i < 2*MPU_MAXWAIT; i++) { 151 if (!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY) && 152 bus_space_read_1(iot, ioh, MPU_DATA) == MPU_ACK) { 153 return 0; 154 } 155 } 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 KASSERT(mutex_owned(sc->lock)); 169 170 if (sc->open) 171 return EBUSY; 172 #ifndef AUDIO_NO_POWER_CTL 173 if (sc->powerctl) 174 sc->powerctl(sc->powerarg, 1); 175 #endif 176 if (mpu_reset(sc) != 0) { 177 #ifndef AUDIO_NO_POWER_CTL 178 if (sc->powerctl) 179 sc->powerctl(sc->powerarg, 0); 180 #endif 181 return EIO; 182 } 183 184 bus_space_write_1(sc->iot, sc->ioh, MPU_COMMAND, MPU_UART_MODE); 185 sc->open = 1; 186 sc->intr = iintr; 187 sc->arg = arg; 188 return 0; 189 } 190 191 static void 192 mpu_close(void *addr) 193 { 194 struct mpu_softc *sc = addr; 195 196 DPRINTF(("%s: sc=%p\n", __func__, sc)); 197 198 KASSERT(mutex_owned(sc->lock)); 199 200 sc->open = 0; 201 sc->intr = 0; 202 mpu_reset(sc); /* exit UART mode */ 203 204 #ifndef AUDIO_NO_POWER_CTL 205 if (sc->powerctl) 206 sc->powerctl(sc->powerarg, 0); 207 #endif 208 } 209 210 static void 211 mpu_readinput(struct mpu_softc *sc) 212 { 213 bus_space_tag_t iot = sc->iot; 214 bus_space_handle_t ioh = sc->ioh; 215 int data; 216 217 KASSERT(mutex_owned(sc->lock)); 218 219 while(!(MPU_GETSTATUS(iot, ioh) & MPU_INPUT_EMPTY)) { 220 data = bus_space_read_1(iot, ioh, MPU_DATA); 221 DPRINTFN(3, ("%s: sc=%p 0x%02x\n", __func__, sc, data)); 222 if (sc->intr) 223 sc->intr(sc->arg, data); 224 } 225 } 226 227 static int 228 mpu_output(void *addr, int d) 229 { 230 struct mpu_softc *sc = addr; 231 232 KASSERT(mutex_owned(sc->lock)); 233 234 DPRINTFN(3, ("%s: sc=%p 0x%02x\n", __func__, sc, d)); 235 if (!(MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY)) { 236 mpu_readinput(sc); 237 } 238 if (mpu_waitready(sc)) { 239 DPRINTF(("%s:: not ready\n", __func__)); 240 return EIO; 241 } 242 bus_space_write_1(sc->iot, sc->ioh, MPU_DATA, d); 243 return 0; 244 } 245 246 static void 247 mpu_getinfo(void *addr, struct midi_info *mi) 248 { 249 struct mpu_softc *sc = addr; 250 251 KASSERT(mutex_owned(sc->lock)); 252 253 mi->name = sc->model; 254 mi->props = 0; 255 } 256 257 static void 258 mpu_get_locks(void *addr, kmutex_t **intr, kmutex_t **proc) 259 { 260 struct mpu_softc *sc = addr; 261 262 *intr = sc->lock; 263 *proc = NULL; 264 } 265 266 int 267 mpu_intr(void *addr) 268 { 269 struct mpu_softc *sc = addr; 270 271 KASSERT(mutex_owned(sc->lock)); 272 273 if (MPU_GETSTATUS(sc->iot, sc->ioh) & MPU_INPUT_EMPTY) { 274 DPRINTF(("%s: no data\n", __func__)); 275 return 0; 276 } else { 277 mpu_readinput(sc); 278 return 1; 279 } 280 } 281