xref: /netbsd-src/sys/arch/macppc/dev/mediabay.c (revision 179b12252ecaf3553d9c2b7458ce62b6a2203d0c)
1 /*	$NetBSD: mediabay.c,v 1.18 2009/03/14 21:04:11 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.18 2009/03/14 21:04:11 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(struct device *parent, struct device *self, void *aux)
122 {
123 	struct mediabay_softc *sc = (struct mediabay_softc *)self;
124 	struct confargs *ca = aux;
125 	int irq, itype;
126 
127 	ca->ca_reg[0] += ca->ca_baseaddr;
128 
129 	sc->sc_addr = mapiodev(ca->ca_reg[0], PAGE_SIZE);
130 	sc->sc_node = ca->ca_node;
131 	sc->sc_baseaddr = ca->ca_baseaddr;
132 	sc->sc_tag = ca->ca_tag;
133 	irq = ca->ca_intr[0];
134 	itype = IST_EDGE;
135 
136 	if (of_compatible(ca->ca_node, mediabay_keylargo) != -1) {
137 		sc->sc_type = MB_CONTROLLER_KEYLARGO;
138 		sc->sc_fcr = sc->sc_addr + 2;
139 	} else {
140 		sc->sc_type = MB_CONTROLLER_OTHER;
141 		sc->sc_fcr = sc->sc_addr + 1;
142 	}
143 
144 	if (ca->ca_nintr == 8 && ca->ca_intr[1] != 0)
145 		itype = IST_LEVEL;
146 
147 	printf(" irq %d %s\n", irq, intr_typename(itype));
148 
149 	intr_establish(irq, itype, IPL_BIO, mediabay_intr, sc);
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
161 mediabay_attach_content(struct mediabay_softc *sc)
162 {
163 	int child;
164 	u_int fcr = 0;
165 	struct device *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", sc->sc_dev.dv_xname);
192 			delay(2000000);
193 		}
194 	} else {
195 		printf("%s: powering up keylargo-media-bay..", sc->sc_dev.dv_xname);
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");
217 		delay(50000);
218 
219 		out32rb(sc->sc_addr, in32rb(sc->sc_addr) & ~0xf);
220 		__asm volatile ("eieio");
221 		delay(50000);
222 
223 		tsleep(sc, PRI_NONE, "mediabay", hz*1);
224 
225 		printf(" done.\n");
226 	}
227 
228 	for (child = OF_child(sc->sc_node); child; child = OF_peer(child)) {
229 		memset(name, 0, sizeof(name));
230 		if (OF_getprop(child, "name", name, sizeof(name)) == -1)
231 			continue;
232 		ca.ca_name = name;
233 		ca.ca_node = child;
234 		ca.ca_baseaddr = sc->sc_baseaddr;
235 		ca.ca_tag = sc->sc_tag;
236 
237 		ca.ca_nreg  = OF_getprop(child, "reg", reg, sizeof(reg));
238 		ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr,
239 				sizeof(intr));
240 		if (ca.ca_nintr == -1)
241 			ca.ca_nintr = OF_getprop(child, "interrupts", intr,
242 					sizeof(intr));
243 		ca.ca_reg = reg;
244 		ca.ca_intr = intr;
245 
246 		content = config_found(&sc->sc_dev, &ca, mediabay_print);
247 		if (content) {
248 			sc->sc_content = content;
249 			return;
250 		}
251 	}
252 
253 	if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
254 		/* No devices found.  Disable media-bay. */
255 		fcr &= ~(FCR_MEDIABAY_ENABLE | FCR_MEDIABAY_IDE_ENABLE |
256 			 FCR_MEDIABAY_CD_POWER | FCR_MEDIABAY_FD_ENABLE);
257 		out32rb(sc->sc_fcr, fcr);
258 	}
259 }
260 
261 int
262 mediabay_print(void *aux, const char *mediabay)
263 {
264 	struct confargs *ca = aux;
265 
266 	if (mediabay == NULL && ca->ca_nreg > 0)
267 		aprint_normal(" offset 0x%x", ca->ca_reg[0]);
268 
269 	return QUIET;
270 }
271 
272 int
273 mediabay_intr(void *v)
274 {
275 	struct mediabay_softc *sc = v;
276 
277 	wakeup(&sc->sc_kthread);
278 
279 	return 1;
280 }
281 
282 void
283 mediabay_kthread(void *v)
284 {
285 	struct mediabay_softc *sc = v;
286 	u_int x, fcr;
287 
288 	for (;;) {
289 		tsleep(&sc->sc_kthread, PRIBIO, "mbayev", 0);
290 
291 		/* sleep 0.25 sec */
292 		tsleep(mediabay_kthread, PRIBIO, "mbayev", hz/4);
293 
294 		DPRINTF("%s: ", sc->sc_dev.dv_xname);
295 		x = in32rb(sc->sc_addr);
296 
297 		switch (MEDIABAY_ID(sc, x)) {
298 		case MEDIABAY_ID_NONE:
299 			DPRINTF("removed\n");
300 			if (sc->sc_content != NULL) {
301 				config_detach(sc->sc_content, DETACH_FORCE);
302 				DPRINTF("%s: detach done\n",
303 					sc->sc_dev.dv_xname);
304 				sc->sc_content = NULL;
305 
306 				if (sc->sc_type != MB_CONTROLLER_KEYLARGO) {
307 					/* disable media-bay */
308 					fcr = in32rb(sc->sc_fcr);
309 					fcr &= ~(FCR_MEDIABAY_ENABLE |
310 						 FCR_MEDIABAY_IDE_ENABLE |
311 						 FCR_MEDIABAY_CD_POWER |
312 						 FCR_MEDIABAY_FD_ENABLE);
313 					out32rb(sc->sc_fcr, fcr);
314 				}
315 			}
316 			break;
317 		case MEDIABAY_ID_FD:
318 			DPRINTF("FD inserted\n");
319 			break;
320 		case MEDIABAY_ID_CD:
321 			DPRINTF("CD inserted\n");
322 
323 			if (sc->sc_content == NULL)
324 				mediabay_attach_content(sc);
325 			break;
326 		default:
327 			printf("unknown event (0x%x)\n", x);
328 		}
329 	}
330 }
331 
332 /* PBG3: 0x7025X0c0 */
333 /* 3400: 0x7070X080 */
334 /* 2400: 0x0070X0a8 */
335