mediabay.c revision 1.17 1 /* $NetBSD: mediabay.c,v 1.17 2009/03/14 15:36:09 dsl 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.17 2009/03/14 15:36:09 dsl 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(struct device *, struct device *, void *);
68 int mediabay_match(struct device *, struct cfdata *, void *);
69 int mediabay_print(void *, const char *);
70 void mediabay_attach_content(struct mediabay_softc *);
71 int mediabay_intr(void *);
72 void mediabay_kthread(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(struct device *parent, struct cfdata *cf, void *aux)
108 {
109 struct confargs *ca = aux;
110
111 if (strcmp(ca->ca_name, "media-bay") == 0)
112 return 1;
113
114 return 0;
115 }
116
117 /*
118 * Attach all the sub-devices we can find
119 */
120 void
121 mediabay_attach(parent, self, aux)
122 struct device *parent, *self;
123 void *aux;
124 {
125 struct mediabay_softc *sc = (struct mediabay_softc *)self;
126 struct confargs *ca = aux;
127 int irq, itype;
128
129 ca->ca_reg[0] += ca->ca_baseaddr;
130
131 sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE);
132 sc->sc_node = ca->ca_node;
133 sc->sc_baseaddr = ca->ca_baseaddr;
134 sc->sc_tag = ca->ca_tag;
135 irq = ca->ca_intr[0];
136 itype = IST_EDGE;
137
138 if (of_compatible(ca->ca_node, mediabay_keylargo) != -1) {
139 sc->sc_type = MB_CONTROLLER_KEYLARGO;
140 sc->sc_fcr = sc->sc_addr + 2;
141 } else {
142 sc->sc_type = MB_CONTROLLER_OTHER;
143 sc->sc_fcr = sc->sc_addr + 1;
144 }
145
146 if (ca->ca_nintr == 8 && ca->ca_intr[1] != 0)
147 itype = IST_LEVEL;
148
149 printf(" irq %d %s\n", irq, intr_typename(itype));
150
151 intr_establish(irq, itype, IPL_BIO, mediabay_intr, sc);
152
153 sc->sc_content = NULL;
154
155 if (MEDIABAY_ID(sc, in32rb(sc->sc_addr)) != MEDIABAY_ID_NONE)
156 mediabay_attach_content(sc);
157
158 kthread_create(PRI_NONE, 0, NULL, mediabay_kthread, sc,
159 &sc->sc_kthread, "media-bay");
160 }
161
162 void
163 mediabay_attach_content(struct mediabay_softc *sc)
164 {
165 int child;
166 u_int fcr = 0;
167 struct device *content;
168 struct confargs ca;
169 u_int reg[20], intr[5];
170 char name[32];
171
172 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
173 fcr = in32rb(sc->sc_fcr);
174
175 /*
176 * if the mediabay isn't powered up we need to wait a few seconds
177 * before probing devices
178 */
179 if ((fcr & (FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE
180 | FCR_MEDIABAY_CD_POWER)) != (FCR_MEDIABAY_ENABLE
181 | FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER)) {
182 fcr |= FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_RESET;
183 out32rb(sc->sc_fcr, fcr);
184 delay(50000);
185
186 fcr &= ~FCR_MEDIABAY_RESET;
187 out32rb(sc->sc_fcr, fcr);
188 delay(50000);
189
190 fcr |= FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER;
191 out32rb(sc->sc_fcr, fcr);
192 delay(50000);
193 printf("%s: powering up...\n", sc->sc_dev.dv_xname);
194 delay(2000000);
195 }
196 } else {
197 printf("%s: powering up keylargo-media-bay..", sc->sc_dev.dv_xname);
198
199 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_RESET);
200 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_POWER);
201 delay(50000);
202
203 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_ENABLE);
204 delay(50000);
205
206 out32rb(sc->sc_addr,
207 in32rb(sc->sc_addr) | MBCR_MEDIABAY0_IDE_ENABLE);
208 delay(50000);
209
210 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_RESET);
211 delay(50000);
212
213 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_RESET);
214 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_ENABLE);
215 delay(50000);
216
217 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_ENABLE);
218 __asm volatile ("eieio");
219 delay(50000);
220
221 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~0xf);
222 __asm volatile ("eieio");
223 delay(50000);
224
225 tsleep(sc, PRI_NONE, "mediabay", hz*1);
226
227 printf(" done.\n");
228 }
229
230 for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
231 memset(name, 0, sizeof(name));
232 if (OF_getprop(child, "name", name, sizeof(name)) == -1)
233 continue;
234 ca.ca_name = name;
235 ca.ca_node = child;
236 ca.ca_baseaddr = sc->sc_baseaddr;
237 ca.ca_tag = sc->sc_tag;
238
239 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
240 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
241 sizeof(intr));
242 if (ca.ca_nintr == -1)
243 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
244 sizeof(intr));
245 ca.ca_reg = reg;
246 ca.ca_intr = intr;
247
248 content = config_found(&sc->sc_dev, &ca, mediabay_print);
249 if (content) {
250 sc->sc_content = content;
251 return;
252 }
253 }
254
255 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
256 /* No devices found. Disable media-bay. */
257 fcr &= ~(FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE |
258 FCR_MEDIABAY_CD_POWER | FCR_MEDIABAY_FD_ENABLE);
259 out32rb(sc->sc_fcr, fcr);
260 }
261 }
262
263 int
264 mediabay_print(void *aux, const char *mediabay)
265 {
266 struct confargs *ca = aux;
267
268 if (mediabay == NULL && ca->ca_nreg > 0)
269 aprint_normal(" offset 0x%x", ca->ca_reg[0]);
270
271 return QUIET;
272 }
273
274 int
275 mediabay_intr(void *v)
276 {
277 struct mediabay_softc *sc = v;
278
279 wakeup(&sc->sc_kthread);
280
281 return 1;
282 }
283
284 void
285 mediabay_kthread(void *v)
286 {
287 struct mediabay_softc *sc = v;
288 u_int x, fcr;
289
290 for (;;) {
291 tsleep(&sc->sc_kthread, PRIBIO, "mbayev", 0);
292
293 /* sleep 0.25 sec */
294 tsleep(mediabay_kthread, PRIBIO, "mbayev", hz/4);
295
296 DPRINTF("%s: ", sc->sc_dev.dv_xname);
297 x = in32rb(sc->sc_addr);
298
299 switch (MEDIABAY_ID(sc, x)) {
300 case MEDIABAY_ID_NONE:
301 DPRINTF("removed\n");
302 if (sc->sc_content != NULL) {
303 config_detach(sc->sc_content, DETACH_FORCE);
304 DPRINTF("%s: detach done\n",
305 sc->sc_dev.dv_xname);
306 sc->sc_content = NULL;
307
308 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
309 /* disable media-bay */
310 fcr = in32rb(sc->sc_fcr);
311 fcr &= ~(FCR_MEDIABAY_ENABLE |
312 FCR_MEDIABAY_IDE_ENABLE |
313 FCR_MEDIABAY_CD_POWER |
314 FCR_MEDIABAY_FD_ENABLE);
315 out32rb(sc->sc_fcr, fcr);
316 }
317 }
318 break;
319 case MEDIABAY_ID_FD:
320 DPRINTF("FD inserted\n");
321 break;
322 case MEDIABAY_ID_CD:
323 DPRINTF("CD inserted\n");
324
325 if (sc->sc_content == NULL)
326 mediabay_attach_content(sc);
327 break;
328 default:
329 printf("unknown event (0x%x)\n", x);
330 }
331 }
332 }
333
334 /* PBG3: 0x7025X0c0 */
335 /* 3400: 0x7070X080 */
336 /* 2400: 0x0070X0a8 */
337