mediabay.c revision 1.20 1 /* $NetBSD: mediabay.c,v 1.20 2011/06/18 08:08:28 matt Exp $ */
2
3 /*-
4 * Copyright (C) 1999 Tsubai Masanari. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: mediabay.c,v 1.20 2011/06/18 08:08:28 matt Exp $");
31
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/systm.h>
37
38 #include <dev/ofw/openfirm.h>
39
40 #include <machine/autoconf.h>
41 #include <machine/pio.h>
42
43 enum mediabay_controller {
44 MB_CONTROLLER_KEYLARGO,
45 MB_CONTROLLER_OTHER
46 };
47
48 struct mediabay_softc {
49 struct device sc_dev;
50 bus_space_tag_t sc_tag;
51 int sc_node;
52 u_int *sc_addr;
53 u_int *sc_fcr;
54 u_int sc_baseaddr;
55 device_t sc_content;
56 lwp_t *sc_kthread;
57 enum mediabay_controller sc_type;
58 };
59
60 static const char *mediabay_keylargo[] = {
61 "keylargo-media-bay",
62 NULL
63 };
64
65 void mediabay_attach(device_t, device_t, void *);
66 int mediabay_match(device_t, cfdata_t, void *);
67 int mediabay_print(void *, const char *);
68 void mediabay_attach_content(struct mediabay_softc *);
69 int mediabay_intr(void *);
70 void mediabay_kthread(void *);
71
72 CFATTACH_DECL(mediabay, sizeof(struct mediabay_softc),
73 mediabay_match, mediabay_attach, NULL, NULL);
74
75 #ifdef MEDIABAY_DEBUG
76 # define DPRINTF printf
77 #else
78 # define DPRINTF while (0) printf
79 #endif
80
81 #define FCR_MEDIABAY_RESET 0x00000002
82 #define FCR_MEDIABAY_IDE_ENABLE 0x00000008
83 #define FCR_MEDIABAY_FD_ENABLE 0x00000010
84 #define FCR_MEDIABAY_ENABLE 0x00000080
85 #define FCR_MEDIABAY_CD_POWER 0x00800000
86
87 #define MBCR_MEDIABAY0_ENABLE 0x00000100
88 #define MBCR_MEDIABAY0_RESET 0x00000200
89 #define MBCR_MEDIABAY0_POWER 0x00000400
90 #define MBCR_MEDIABAY0_IDE_ENABLE 0x00001000
91 #define MBCR_MEDIABAY0_DEVMASK 0x00007800
92
93 #define FCR1_EIDE0_ENABLE 0x00800000
94 #define FCR1_EIDE0_RESET 0x01000000
95
96 #define MEDIABAY_ID(sc, x) \
97 ((sc)->sc_type == MB_CONTROLLER_KEYLARGO ? \
98 (((x) >> 4) & 0xf) : \
99 (((x) >> 12) & 0xf))
100 #define MEDIABAY_ID_FD 0
101 #define MEDIABAY_ID_CD 3
102 #define MEDIABAY_ID_NONE 7
103
104 int
105 mediabay_match(device_t parent, cfdata_t cf, void *aux)
106 {
107 struct confargs *ca = aux;
108
109 if (strcmp(ca->ca_name, "media-bay") == 0)
110 return 1;
111
112 return 0;
113 }
114
115 /*
116 * Attach all the sub-devices we can find
117 */
118 void
119 mediabay_attach(device_t parent, device_t self, void *aux)
120 {
121 struct mediabay_softc *sc = device_private(self);
122 struct confargs *ca = aux;
123 int irq, itype;
124
125 ca->ca_reg[0] += ca->ca_baseaddr;
126
127 sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE);
128 sc->sc_node = ca->ca_node;
129 sc->sc_baseaddr = ca->ca_baseaddr;
130 sc->sc_tag = ca->ca_tag;
131 irq = ca->ca_intr[0];
132 itype = IST_EDGE;
133
134 if (of_compatible(ca->ca_node, mediabay_keylargo) != -1) {
135 sc->sc_type = MB_CONTROLLER_KEYLARGO;
136 sc->sc_fcr = sc->sc_addr + 2;
137 } else {
138 sc->sc_type = MB_CONTROLLER_OTHER;
139 sc->sc_fcr = sc->sc_addr + 1;
140 }
141
142 if (ca->ca_nintr == 8 && ca->ca_intr[1] != 0)
143 itype = IST_LEVEL;
144
145 printf(" irq %d %s\n", irq, intr_typename(itype));
146
147 intr_establish(irq, itype, IPL_BIO, mediabay_intr, sc);
148
149 sc->sc_content = NULL;
150
151 if (MEDIABAY_ID(sc, in32rb(sc->sc_addr)) != MEDIABAY_ID_NONE)
152 mediabay_attach_content(sc);
153
154 kthread_create(PRI_NONE, 0, NULL, mediabay_kthread, sc,
155 &sc->sc_kthread, "media-bay");
156 }
157
158 void
159 mediabay_attach_content(struct mediabay_softc *sc)
160 {
161 int child;
162 u_int fcr = 0;
163 device_t content;
164 struct confargs ca;
165 u_int reg[20], intr[5];
166 char name[32];
167
168 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
169 fcr = in32rb(sc->sc_fcr);
170
171 /*
172 * if the mediabay isn't powered up we need to wait a few seconds
173 * before probing devices
174 */
175 if ((fcr & (FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE
176 | FCR_MEDIABAY_CD_POWER)) != (FCR_MEDIABAY_ENABLE
177 | FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER)) {
178 fcr |= FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_RESET;
179 out32rb(sc->sc_fcr, fcr);
180 delay(50000);
181
182 fcr &= ~FCR_MEDIABAY_RESET;
183 out32rb(sc->sc_fcr, fcr);
184 delay(50000);
185
186 fcr |= FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER;
187 out32rb(sc->sc_fcr, fcr);
188 delay(50000);
189 printf("%s: powering up...\n", sc->sc_dev.dv_xname);
190 delay(2000000);
191 }
192 } else {
193 printf("%s: powering up keylargo-media-bay..", sc->sc_dev.dv_xname);
194
195 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_RESET);
196 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_POWER);
197 delay(50000);
198
199 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_ENABLE);
200 delay(50000);
201
202 out32rb(sc->sc_addr,
203 in32rb(sc->sc_addr) | MBCR_MEDIABAY0_IDE_ENABLE);
204 delay(50000);
205
206 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_RESET);
207 delay(50000);
208
209 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_RESET);
210 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_ENABLE);
211 delay(50000);
212
213 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_ENABLE);
214 __asm volatile ("eieio");
215 delay(50000);
216
217 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~0xf);
218 __asm volatile ("eieio");
219 delay(50000);
220
221 tsleep(sc, PRI_NONE, "mediabay", hz*1);
222
223 printf(" done.\n");
224 }
225
226 for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
227 memset(name, 0, sizeof(name));
228 if (OF_getprop(child, "name", name, sizeof(name)) == -1)
229 continue;
230 ca.ca_name = name;
231 ca.ca_node = child;
232 ca.ca_baseaddr = sc->sc_baseaddr;
233 ca.ca_tag = sc->sc_tag;
234
235 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
236 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
237 sizeof(intr));
238 if (ca.ca_nintr == -1)
239 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
240 sizeof(intr));
241 ca.ca_reg = reg;
242 ca.ca_intr = intr;
243
244 content = config_found(&sc->sc_dev, &ca, mediabay_print);
245 if (content) {
246 sc->sc_content = content;
247 return;
248 }
249 }
250
251 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
252 /* No devices found. Disable media-bay. */
253 fcr &= ~(FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE |
254 FCR_MEDIABAY_CD_POWER | FCR_MEDIABAY_FD_ENABLE);
255 out32rb(sc->sc_fcr, fcr);
256 }
257 }
258
259 int
260 mediabay_print(void *aux, const char *mediabay)
261 {
262 struct confargs *ca = aux;
263
264 if (mediabay == NULL && ca->ca_nreg > 0)
265 aprint_normal(" offset 0x%x", ca->ca_reg[0]);
266
267 return QUIET;
268 }
269
270 int
271 mediabay_intr(void *v)
272 {
273 struct mediabay_softc *sc = v;
274
275 wakeup(&sc->sc_kthread);
276
277 return 1;
278 }
279
280 void
281 mediabay_kthread(void *v)
282 {
283 struct mediabay_softc *sc = v;
284 u_int x, fcr;
285
286 for (;;) {
287 tsleep(&sc->sc_kthread, PRIBIO, "mbayev", 0);
288
289 /* sleep 0.25 sec */
290 tsleep(mediabay_kthread, PRIBIO, "mbayev", hz/4);
291
292 DPRINTF("%s: ", sc->sc_dev.dv_xname);
293 x = in32rb(sc->sc_addr);
294
295 switch (MEDIABAY_ID(sc, x)) {
296 case MEDIABAY_ID_NONE:
297 DPRINTF("removed\n");
298 if (sc->sc_content != NULL) {
299 config_detach(sc->sc_content, DETACH_FORCE);
300 DPRINTF("%s: detach done\n",
301 sc->sc_dev.dv_xname);
302 sc->sc_content = NULL;
303
304 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
305 /* disable media-bay */
306 fcr = in32rb(sc->sc_fcr);
307 fcr &= ~(FCR_MEDIABAY_ENABLE |
308 FCR_MEDIABAY_IDE_ENABLE |
309 FCR_MEDIABAY_CD_POWER |
310 FCR_MEDIABAY_FD_ENABLE);
311 out32rb(sc->sc_fcr, fcr);
312 }
313 }
314 break;
315 case MEDIABAY_ID_FD:
316 DPRINTF("FD inserted\n");
317 break;
318 case MEDIABAY_ID_CD:
319 DPRINTF("CD inserted\n");
320
321 if (sc->sc_content == NULL)
322 mediabay_attach_content(sc);
323 break;
324 default:
325 printf("unknown event (0x%x)\n", x);
326 }
327 }
328 }
329
330 /* PBG3: 0x7025X0c0 */
331 /* 3400: 0x7070X080 */
332 /* 2400: 0x0070X0a8 */
333