1 /* $NetBSD: mediabay.c,v 1.28 2022/02/16 23:49:26 riastradh 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.28 2022/02/16 23:49:26 riastradh 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 device_t 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_NEW(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
mediabay_match(device_t parent,cfdata_t cf,void * aux)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
mediabay_attach(device_t parent,device_t self,void * aux)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 sc->sc_dev = self;
126 ca->ca_reg[0] += ca->ca_baseaddr;
127
128 sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE, false);
129 sc->sc_node = ca->ca_node;
130 sc->sc_baseaddr = ca->ca_baseaddr;
131 sc->sc_tag = ca->ca_tag;
132 irq = ca->ca_intr[0];
133 itype = IST_EDGE;
134
135 if (of_compatible(ca->ca_node, mediabay_keylargo)) {
136 sc->sc_type = MB_CONTROLLER_KEYLARGO;
137 sc->sc_fcr = sc->sc_addr + 2;
138 } else {
139 sc->sc_type = MB_CONTROLLER_OTHER;
140 sc->sc_fcr = sc->sc_addr + 1;
141 }
142
143 if (ca->ca_nintr == 8 && ca->ca_intr[1] != 0)
144 itype = IST_LEVEL;
145
146 printf(" irq %d %s\n", irq, intr_typename(itype));
147
148 intr_establish_xname(irq, itype, IPL_BIO, mediabay_intr, sc,
149 device_xname(self));
150
151 sc->sc_content = NULL;
152
153 if (MEDIABAY_ID(sc, in32rb(sc->sc_addr)) != MEDIABAY_ID_NONE)
154 mediabay_attach_content(sc);
155
156 kthread_create(PRI_NONE, 0, NULL, mediabay_kthread, sc,
157 &sc->sc_kthread, "media-bay");
158 }
159
160 void
mediabay_attach_content(struct mediabay_softc * sc)161 mediabay_attach_content(struct mediabay_softc *sc)
162 {
163 int child;
164 u_int fcr = 0;
165 device_t content;
166 struct confargs ca;
167 u_int reg[20], intr[5];
168 char name[32];
169
170 if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
171 fcr = in32rb(sc->sc_fcr);
172
173 /*
174 * if the mediabay isn't powered up we need to wait a few seconds
175 * before probing devices
176 */
177 if ((fcr & (FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE
178 | FCR_MEDIABAY_CD_POWER)) != (FCR_MEDIABAY_ENABLE
179 | FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER)) {
180 fcr |= FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_RESET;
181 out32rb(sc->sc_fcr, fcr);
182 delay(50000);
183
184 fcr &= ~FCR_MEDIABAY_RESET;
185 out32rb(sc->sc_fcr, fcr);
186 delay(50000);
187
188 fcr |= FCR_MEDIABAY_IDE_ENABLE | FCR_MEDIABAY_CD_POWER;
189 out32rb(sc->sc_fcr, fcr);
190 delay(50000);
191 printf("%s: powering up...\n", device_xname(sc->sc_dev));
192 delay(2000000);
193 }
194 } else {
195 printf("%s: powering up keylargo-media-bay..", device_xname(sc->sc_dev));
196
197 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_RESET);
198 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_POWER);
199 delay(50000);
200
201 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~MBCR_MEDIABAY0_ENABLE);
202 delay(50000);
203
204 out32rb(sc->sc_addr,
205 in32rb(sc->sc_addr) | MBCR_MEDIABAY0_IDE_ENABLE);
206 delay(50000);
207
208 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_RESET);
209 delay(50000);
210
211 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_RESET);
212 out32rb(sc->sc_fcr, in32rb(sc->sc_fcr) | FCR1_EIDE0_ENABLE);
213 delay(50000);
214
215 out32rb(sc->sc_addr, in32rb(sc->sc_addr) | MBCR_MEDIABAY0_ENABLE);
216 __asm volatile("eieio" ::: "memory");
217 delay(50000);
218
219 out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~0xf);
220 __asm volatile("eieio" ::: "memory");
221 delay(50000);
222
223 tsleep(sc, PRI_NONE, "mediabay", hz*1);
224
225 printf(" done.\n");
226 }
227
228 devhandle_t selfh = device_handle(sc->sc_dev);
229 for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
230 memset(name, 0, sizeof(name));
231 if (OF_getprop(child, "name", name, sizeof(name)) == -1)
232 continue;
233 ca.ca_name = name;
234 ca.ca_node = child;
235 ca.ca_baseaddr = sc->sc_baseaddr;
236 ca.ca_tag = sc->sc_tag;
237
238 ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg));
239 ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
240 sizeof(intr));
241 if (ca.ca_nintr == -1)
242 ca.ca_nintr = OF_getprop(child, "interrupts", intr,
243 sizeof(intr));
244 ca.ca_reg = reg;
245 ca.ca_intr = intr;
246
247 content = config_found(sc->sc_dev, &ca, mediabay_print,
248 CFARGS(.devhandle = devhandle_from_of(selfh, child)));
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
mediabay_print(void * aux,const char * mediabay)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
mediabay_intr(void * v)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
mediabay_kthread(void * v)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: ", device_xname(sc->sc_dev));
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 device_xname(sc->sc_dev));
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