mediabay.c revision 1.15 1 /* $NetBSD: mediabay.c,v 1.15 2008/08/28 04:05:50 jmcneill 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.15 2008/08/28 04:05:50 jmcneill 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 <uvm/uvm_extern.h>
39
40 #include <dev/ofw/openfirm.h>
41
42 #include <machine/autoconf.h>
43 #include <machine/pio.h>
44
45 enum mediabay_controller {
46 MB_CONTROLLER_KEYLARGO,
47 MB_CONTROLLER_OTHER
48 };
49
50 struct mediabay_softc {
51 struct device sc_dev;
52 bus_space_tag_t sc_tag;
53 int sc_node;
54 u_int *sc_addr;
55 u_int *sc_fcr;
56 u_int sc_baseaddr;
57 struct device *sc_content;
58 lwp_t *sc_kthread;
59 enum mediabay_controller sc_type;
60 };
61
62 static const char *mediabay_keylargo[] = {
63 "keylargo-media-bay",
64 NULL
65 };
66
67 void mediabay_attach __P((struct device *, struct device *, void *));
68 int mediabay_match __P((struct device *, struct cfdata *, void *));
69 int mediabay_print __P((void *, const char *));
70 void mediabay_attach_content __P((struct mediabay_softc *));
71 int mediabay_intr __P((void *));
72 void mediabay_kthread __P((void *));
73
74 CFATTACH_DECL(mediabay, sizeof(struct mediabay_softc),
75 mediabay_match, mediabay_attach, NULL, NULL);
76
77 #ifdef MEDIABAY_DEBUG
78 # define DPRINTF printf
79 #else
80 # define DPRINTF while (0) printf
81 #endif
82
83 #define FCR_MEDIABAY_RESET 0x00000002
84 #define FCR_MEDIABAY_IDE_ENABLE 0x00000008
85 #define FCR_MEDIABAY_FD_ENABLE 0x00000010
86 #define FCR_MEDIABAY_ENABLE 0x00000080
87 #define FCR_MEDIABAY_CD_POWER 0x00800000
88
89 #define MBCR_MEDIABAY0_ENABLE 0x00000100
90 #define MBCR_MEDIABAY0_RESET 0x00000200
91 #define MBCR_MEDIABAY0_POWER 0x00000400
92 #define MBCR_MEDIABAY0_IDE_ENABLE 0x00001000
93 #define MBCR_MEDIABAY0_DEVMASK 0x00007800
94
95 #define FCR1_EIDE0_ENABLE 0x00800000
96 #define FCR1_EIDE0_RESET 0x01000000
97
98 #define MEDIABAY_ID(sc, x) \
99 ((sc)->sc_type == MB_CONTROLLER_KEYLARGO ? \
100 (((x) >> 4) & 0xf) : \
101 (((x) >> 12) & 0xf))
102 #define MEDIABAY_ID_FD 0
103 #define MEDIABAY_ID_CD 3
104 #define MEDIABAY_ID_NONE 7
105
106 int
107 mediabay_match(parent, cf, aux)
108 struct device *parent;
109 struct cfdata *cf;
110 void *aux;
111 {
112 struct confargs *ca = aux;
113
114 if (strcmp(ca->ca_name, "media-bay") == 0)
115 return 1;
116
117 return 0;
118 }
119
120 /*
121 * Attach all the sub-devices we can find
122 */
123 void
124 mediabay_attach(parent, self, aux)
125 struct device *parent, *self;
126 void *aux;
127 {
128 struct mediabay_softc *sc = (struct mediabay_softc *)self;
129 struct confargs *ca = aux;
130 int irq, itype;
131
132 ca->ca_reg[0] += ca->ca_baseaddr;
133
134 sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE);
135 sc->sc_node = ca->ca_node;
136 sc->sc_baseaddr = ca->ca_baseaddr;
137 sc->sc_tag = ca->ca_tag;
138 irq = ca->ca_intr[0];
139 itype = IST_EDGE;
140
141 if (of_compatible(ca->ca_node, mediabay_keylargo) != -1) {
142 sc->sc_type = MB_CONTROLLER_KEYLARGO;
143 sc->sc_fcr = sc->sc_addr + 2;
144 } else {
145 sc->sc_type = MB_CONTROLLER_OTHER;
146 sc->sc_fcr = sc->sc_addr + 1;
147 }
148
149 if (ca->ca_nintr == 8 && ca->ca_intr[1] != 0)
150 itype = IST_LEVEL;
151
152 printf(" irq %d %s\n", irq, intr_typename(itype));
153
154 intr_establish(irq, itype, IPL_BIO, mediabay_intr, sc);
155
156 sc->sc_content = NULL;
157
158 if (MEDIABAY_ID(sc, in32rb(sc->sc_addr)) != MEDIABAY_ID_NONE)
159 mediabay_attach_content(sc);
160
161 kthread_create(PRI_NONE, 0, NULL, mediabay_kthread, sc,
162 &sc->sc_kthread, "media-bay");
163 }
164
165 void
166 mediabay_attach_content(sc)
167 struct mediabay_softc *sc;
168 {
169 int child;
170 u_int fcr = 0;
171 struct device *content;
172 struct confargs ca;
173 u_int reg[20], intr[5];
174 char name[32];
175
176 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
177 fcr = in32rb(sc->sc_fcr);
178
179 /*
180 * if the mediabay isn't powered up we need to wait a few seconds
181 * before probing devices
182 */
183 if ((fcr & (FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE
184 | FCR_MEDIABAY_CD_POWER)) != (FCR_MEDIABAY_ENABLE
185 | FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER)) {
186 fcr |= FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_RESET;
187 out32rb(sc->sc_fcr, fcr);
188 delay(50000);
189
190 fcr &= ~FCR_MEDIABAY_RESET;
191 out32rb(sc->sc_fcr, fcr);
192 delay(50000);
193
194 fcr |= FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER;
195 out32rb(sc->sc_fcr, fcr);
196 delay(50000);
197 printf("%s: powering up...\n", sc->sc_dev.dv_xname);
198 delay(2000000);
199 }
200 } else {
201 printf("%s: powering up keylargo-media-bay..", sc->sc_dev.dv_xname);
202
203 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_RESET);
204 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_POWER);
205 delay(50000);
206
207 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_ENABLE);
208 delay(50000);
209
210 out32rb(sc->sc_addr,
211 in32rb(sc->sc_addr) | MBCR_MEDIABAY0_IDE_ENABLE);
212 delay(50000);
213
214 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_RESET);
215 delay(50000);
216
217 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_RESET);
218 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_ENABLE);
219 delay(50000);
220
221 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_ENABLE);
222 __asm volatile ("eieio");
223 delay(50000);
224
225 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~0xf);
226 __asm volatile ("eieio");
227 delay(50000);
228
229 tsleep(sc, PRI_NONE, "mediabay", hz*1);
230
231 printf(" done.\n");
232 }
233
234 for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
235 memset(name, 0, sizeof(name));
236 if (OF_getprop(child, "name", name, sizeof(name)) == -1)
237 continue;
238 ca.ca_name = name;
239 ca.ca_node = child;
240 ca.ca_baseaddr = sc->sc_baseaddr;
241 ca.ca_tag = sc->sc_tag;
242
243 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
244 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
245 sizeof(intr));
246 if (ca.ca_nintr == -1)
247 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
248 sizeof(intr));
249 ca.ca_reg = reg;
250 ca.ca_intr = intr;
251
252 content = config_found(&sc->sc_dev, &ca, mediabay_print);
253 if (content) {
254 sc->sc_content = content;
255 return;
256 }
257 }
258
259 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
260 /* No devices found. Disable media-bay. */
261 fcr &= ~(FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE |
262 FCR_MEDIABAY_CD_POWER | FCR_MEDIABAY_FD_ENABLE);
263 out32rb(sc->sc_fcr, fcr);
264 }
265 }
266
267 int
268 mediabay_print(aux, mediabay)
269 void *aux;
270 const char *mediabay;
271 {
272 struct confargs *ca = aux;
273
274 if (mediabay == NULL && ca->ca_nreg > 0)
275 aprint_normal(" offset 0x%x", ca->ca_reg[0]);
276
277 return QUIET;
278 }
279
280 int
281 mediabay_intr(v)
282 void *v;
283 {
284 struct mediabay_softc *sc = v;
285
286 wakeup(&sc->sc_kthread);
287
288 return 1;
289 }
290
291 void
292 mediabay_kthread(v)
293 void *v;
294 {
295 struct mediabay_softc *sc = v;
296 u_int x, fcr;
297
298 for (;;) {
299 tsleep(&sc->sc_kthread, PRIBIO, "mbayev", 0);
300
301 /* sleep 0.25 sec */
302 tsleep(mediabay_kthread, PRIBIO, "mbayev", hz/4);
303
304 DPRINTF("%s: ", sc->sc_dev.dv_xname);
305 x = in32rb(sc->sc_addr);
306
307 switch (MEDIABAY_ID(sc, x)) {
308 case MEDIABAY_ID_NONE:
309 DPRINTF("removed\n");
310 if (sc->sc_content != NULL) {
311 config_detach(sc->sc_content, DETACH_FORCE);
312 DPRINTF("%s: detach done\n",
313 sc->sc_dev.dv_xname);
314 sc->sc_content = NULL;
315
316 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
317 /* disable media-bay */
318 fcr = in32rb(sc->sc_fcr);
319 fcr &= ~(FCR_MEDIABAY_ENABLE |
320 FCR_MEDIABAY_IDE_ENABLE |
321 FCR_MEDIABAY_CD_POWER |
322 FCR_MEDIABAY_FD_ENABLE);
323 out32rb(sc->sc_fcr, fcr);
324 }
325 }
326 break;
327 case MEDIABAY_ID_FD:
328 DPRINTF("FD inserted\n");
329 break;
330 case MEDIABAY_ID_CD:
331 DPRINTF("CD inserted\n");
332
333 if (sc->sc_content == NULL)
334 mediabay_attach_content(sc);
335 break;
336 default:
337 printf("unknown event (0x%x)\n", x);
338 }
339 }
340 }
341
342 /* PBG3: 0x7025X0c0 */
343 /* 3400: 0x7070X080 */
344 /* 2400: 0x0070X0a8 */
345