xref: /netbsd-src/sys/dev/ic/mpu.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
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