xref: /openbsd/sys/arch/macppc/dev/pm_direct.c (revision 8932bfb7)
1 /*	$OpenBSD: pm_direct.c,v 1.23 2011/05/14 12:01:16 mpi Exp $	*/
2 /*	$NetBSD: pm_direct.c,v 1.9 2000/06/08 22:10:46 tsubai Exp $	*/
3 
4 /*
5  * Copyright (C) 1997 Takashi Hamada
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *  This product includes software developed by Takashi Hamada
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #ifdef DEBUG
35 #ifndef ADB_DEBUG
36 #define ADB_DEBUG
37 #endif
38 #endif
39 
40 /* #define	PM_GRAB_SI	1 */
41 
42 #include <sys/param.h>
43 #include <sys/cdefs.h>
44 #include <sys/device.h>
45 #include <sys/systm.h>
46 
47 #include <machine/cpu.h>
48 
49 #include <dev/adb/adb.h>
50 #include <macppc/dev/adbvar.h>
51 #include <macppc/dev/pm_direct.h>
52 #include <macppc/dev/viareg.h>
53 
54 /* hardware dependent values */
55 #define ADBDelay 100		/* XXX */
56 
57 /* useful macros */
58 #define PM_SR()			read_via_reg(VIA1, vSR)
59 #define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
60 #define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
61 #define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
62 #if 0
63 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x04)
64 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x04)
65 #define PM_IS_ON		(0x02 == (read_via_reg(VIA2, vBufB) & 0x02))
66 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x02))
67 #else
68 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
69 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
70 #define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
71 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
72 #endif
73 
74 /* these values shows that number of data returned after 'send' cmd is sent */
75 signed char pm_send_cmd_type[] = {
76 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
77 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
78 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
79 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
80 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
81 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
82 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
83 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
84 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
85 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
86 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
87 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
88 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
89 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
90 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
91 	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
92 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
93 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
94 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
95 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
96 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
97 	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
98 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
99 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
100 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
101 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
102 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
103 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
104 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
105 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
106 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
107 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
108 };
109 
110 /* these values shows that number of data returned after 'receive' cmd is sent */
111 signed char pm_receive_cmd_type[] = {
112 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
114 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
116 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
118 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
120 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
122 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
124 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
126 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
128 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
130 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
132 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
134 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
136 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
138 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
140 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
141 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
142 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
144 };
145 
146 
147 /*
148  * Define the private functions
149  */
150 
151 /* for debugging */
152 #ifdef ADB_DEBUG
153 void	pm_printerr(char *, int, int, char *);
154 #endif
155 
156 int	pm_wait_busy(int);
157 int	pm_wait_free(int);
158 int	pm_receive(u_char *);
159 int	pm_send(u_char);
160 
161 /* these functions also use the variables of adb_direct.c */
162 void	pm_adb_get_TALK_result(PMData *);
163 void	pm_adb_get_ADB_data(PMData *);
164 
165 
166 /*
167  * These variables are in adb_direct.c.
168  */
169 extern u_char	*adbBuffer;	/* pointer to user data area */
170 extern void	*adbCompRout;	/* pointer to the completion routine */
171 extern void	*adbCompData;	/* pointer to the completion routine data */
172 extern int	adbWaiting;	/* waiting for return data from the device */
173 extern int	adbWaitingCmd;	/* ADB command we are waiting for */
174 extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
175 
176 #define	ADB_MAX_MSG_LENGTH	16
177 #define	ADB_MAX_HDR_LENGTH	8
178 struct adbCommand {
179 	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
180 	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
181 	u_char	*saveBuf;	/* where to save result */
182 	u_char	*compRout;	/* completion routine pointer */
183 	u_char	*compData;	/* completion routine data pointer */
184 	u_int	cmd;		/* the original command for this data */
185 	u_int	unsol;		/* 1 if packet was unsolicited */
186 	u_int	ack_only;	/* 1 for no special processing */
187 };
188 extern	void	adb_pass_up(struct adbCommand *);
189 
190 
191 #ifdef ADB_DEBUG
192 /*
193  * This function dumps contents of the PMData
194  */
195 void
196 pm_printerr(ttl, rval, num, data)
197 	char *ttl;
198 	int rval;
199 	int num;
200 	char *data;
201 {
202 	int i;
203 
204 	printf("pm: %s:%04x %02x ", ttl, rval, num);
205 	for (i = 0; i < num; i++)
206 		printf("%02x ", data[i]);
207 	printf("\n");
208 }
209 #endif
210 
211 /*
212  * Wait until PM IC is busy
213  */
214 int
215 pm_wait_busy(int delay)
216 {
217 	while (PM_IS_ON) {
218 #ifdef PM_GRAB_SI
219 		(void)intr_dispatch(0x70);
220 #endif
221 		if ((--delay) < 0)
222 			return 1;	/* timeout */
223 	}
224 	return 0;
225 }
226 
227 
228 /*
229  * Wait until PM IC is free
230  */
231 int
232 pm_wait_free(int delay)
233 {
234 	while (PM_IS_OFF) {
235 #ifdef PM_GRAB_SI
236 		(void)intr_dispatch(0x70);
237 #endif
238 		if ((--delay) < 0)
239 			return 0;	/* timeout */
240 	}
241 	return 1;
242 }
243 
244 /*
245  * Functions for the PB Duo series and the PB 5XX series
246  */
247 
248 /*
249  * Receive data from PM for the PB Duo series and the PB 5XX series
250  */
251 int
252 pm_receive(u_char *data)
253 {
254 	int i;
255 	int rval;
256 
257 	rval = 0xffffcd34;
258 
259 	switch (1) {
260 	default:
261 		/* set VIA SR to input mode */
262 		via_reg_or(VIA1, vACR, 0x0c);
263 		via_reg_and(VIA1, vACR, ~0x10);
264 		i = PM_SR();
265 
266 		PM_SET_STATE_ACKOFF();
267 		if (pm_wait_busy((int)ADBDelay*32) != 0)
268 			break;		/* timeout */
269 
270 		PM_SET_STATE_ACKON();
271 		rval = 0xffffcd33;
272 		if (pm_wait_free((int)ADBDelay*32) == 0)
273 			break;		/* timeout */
274 
275 		*data = PM_SR();
276 		rval = 0;
277 
278 		break;
279 	}
280 
281 	PM_SET_STATE_ACKON();
282 	via_reg_or(VIA1, vACR, 0x1c);
283 
284 	return rval;
285 }
286 
287 /*
288  * Send data to PM for the PB Duo series and the PB 5XX series
289  */
290 int
291 pm_send(data)
292 	u_char data;
293 {
294 	int rval;
295 
296 	via_reg_or(VIA1, vACR, 0x1c);
297 	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
298 
299 	PM_SET_STATE_ACKOFF();
300 	rval = 0xffffcd36;
301 	if (pm_wait_busy((int)ADBDelay*32) != 0) {
302 		PM_SET_STATE_ACKON();
303 		via_reg_or(VIA1, vACR, 0x1c);
304 		return rval;
305 	}
306 
307 	PM_SET_STATE_ACKON();
308 	rval = 0xffffcd35;
309 	if (pm_wait_free((int)ADBDelay*32) != 0)
310 		rval = 0;
311 
312 	PM_SET_STATE_ACKON();
313 	via_reg_or(VIA1, vACR, 0x1c);
314 
315 	return rval;
316 }
317 
318 
319 
320 /*
321  * My PMgrOp routine for the PB Duo series and the PB 5XX series
322  */
323 int
324 pmgrop(PMData *pmdata)
325 {
326 	int i;
327 	int s;
328 	u_char via1_vIER;
329 	int rval = 0;
330 	int num_pm_data = 0;
331 	u_char pm_cmd;
332 	short pm_num_rx_data;
333 	u_char pm_data;
334 	u_char *pm_buf;
335 
336 	s = splhigh();
337 
338 	/* disable all interrupts but PM */
339 	via1_vIER = 0x10;
340 	via1_vIER &= read_via_reg(VIA1, vIER);
341 	write_via_reg(VIA1, vIER, via1_vIER);
342 	if (via1_vIER != 0x0)
343 		via1_vIER |= 0x80;
344 
345 	switch (pmdata->command) {
346 	default:
347 		/* wait until PM is free */
348 		pm_cmd = (u_char)(pmdata->command & 0xff);
349 		rval = 0xcd38;
350 		if (pm_wait_free(ADBDelay * 4) == 0)
351 			break;			/* timeout */
352 
353 		/* send PM command */
354 		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
355 			break;				/* timeout */
356 
357 		/* send number of PM data */
358 		num_pm_data = pmdata->num_data;
359 		if (pm_send_cmd_type[pm_cmd] < 0) {
360 			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
361 				break;		/* timeout */
362 			pmdata->command = 0;
363 		}
364 		/* send PM data */
365 		pm_buf = (u_char *)pmdata->s_buf;
366 		for (i = 0 ; i < num_pm_data; i++)
367 			if ((rval = pm_send(pm_buf[i])) != 0)
368 				break;			/* timeout */
369 		if (i != num_pm_data)
370 			break;				/* timeout */
371 
372 
373 		/* check if PM will send me data  */
374 		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
375 		pmdata->num_data = pm_num_rx_data;
376 		if (pm_num_rx_data == 0) {
377 			rval = 0;
378 			break;				/* no return data */
379 		}
380 
381 		/* receive PM command */
382 		pm_data = pmdata->command;
383 		pm_num_rx_data--;
384 		if (pm_num_rx_data == 0)
385 			if ((rval = pm_receive(&pm_data)) != 0) {
386 				rval = 0xffffcd37;
387 				break;
388 			}
389 		pmdata->command = pm_data;
390 
391 		/* receive number of PM data */
392 		if (pm_num_rx_data < 0) {
393 			if ((rval = pm_receive(&pm_data)) != 0)
394 				break;		/* timeout */
395 			num_pm_data = pm_data;
396 		} else
397 			num_pm_data = pm_num_rx_data;
398 		pmdata->num_data = num_pm_data;
399 
400 		/* receive PM data */
401 		pm_buf = (u_char *)pmdata->r_buf;
402 		for (i = 0; i < num_pm_data; i++) {
403 			if ((rval = pm_receive(&pm_data)) != 0)
404 				break;			/* timeout */
405 			pm_buf[i] = pm_data;
406 		}
407 
408 		rval = 0;
409 	}
410 
411 	/* restore former value */
412 	write_via_reg(VIA1, vIER, via1_vIER);
413 	splx(s);
414 
415 	return rval;
416 }
417 
418 
419 /*
420  * My PM interrupt routine for the PB Duo series and the PB 5XX series
421  */
422 void
423 pm_intr()
424 {
425 	int s;
426 	int rval;
427 	PMData pmdata;
428 
429 	s = splhigh();
430 
431 	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
432 						/* ask PM what happend */
433 	pmdata.command = 0x78;
434 	pmdata.num_data = 0;
435 	pmdata.s_buf = &pmdata.data[2];
436 	pmdata.r_buf = &pmdata.data[2];
437 	rval = pmgrop(&pmdata);
438 	if (rval != 0) {
439 #ifdef ADB_DEBUG
440 		if (adb_debug)
441 			printf("pm: PM is not ready. error code: %08x\n", rval);
442 #endif
443 		splx(s);
444 		return;
445 	}
446 
447 	switch ((u_int)(pmdata.data[2] & 0xff)) {
448 	case 0x00:		/* 1 sec interrupt? */
449 		break;
450 	case PMU_INT_TICK:	/* 1 sec interrupt? */
451 		break;
452 	case PMU_INT_SNDBRT:	/* Brightness/Contrast button on LCD panel */
453 		break;
454 	case PMU_INT_ADB:	/* ADB data requested by TALK command */
455 	case PMU_INT_ADB|PMU_INT_ADB_AUTO:
456 		pm_adb_get_TALK_result(&pmdata);
457 		break;
458 	case 0x16:		/* ADB device event */
459 	case 0x18:
460 	case 0x1e:
461 	case PMU_INT_WAKEUP:
462 		pm_adb_get_ADB_data(&pmdata);
463 		break;
464 	default:
465 #ifdef ADB_DEBUG
466 		if (adb_debug)
467 			pm_printerr("driver does not support this event.",
468 			    pmdata.data[2], pmdata.num_data,
469 			    pmdata.data);
470 #endif
471 		break;
472 	}
473 
474 	splx(s);
475 }
476 
477 /*
478  * Synchronous ADBOp routine for the Power Manager
479  */
480 int
481 pm_adb_op(u_char *buffer, void *compRout, void *data, int command)
482 {
483 	int i;
484 	int s;
485 	int rval;
486 	int ndelay;
487 	int waitfor;	/* interrupts to poll for */
488 	int ifr;
489 #ifdef ADB_DEBUG
490 	int oldifr;
491 #endif
492 	PMData pmdata;
493 	struct adbCommand packet;
494 	extern int adbempty;
495 
496 	if (adbWaiting == 1)
497 		return 1;
498 
499 	s = splhigh();
500 	write_via_reg(VIA1, vIER, 0x10);
501 
502 	adbBuffer = buffer;
503 	adbCompRout = compRout;
504 	adbCompData = data;
505 
506 	pmdata.command = 0x20;
507 	pmdata.s_buf = pmdata.data;
508 	pmdata.r_buf = pmdata.data;
509 
510 	/*
511 	 * if the command is LISTEN,
512 	 * add number of ADB data to number of PM data
513 	 */
514 	if ((command & 0xc) == 0x8) {
515 		if (buffer != (u_char *)0)
516 			pmdata.num_data = buffer[0] + 3;
517 	} else
518 		pmdata.num_data = 3;
519 
520 	/*
521 	 * Resetting adb on several models, such as
522 	 * - PowerBook3,*
523 	 * - PowerBook5,*
524 	 * - PowerMac10,1
525 	 * causes several pmu interrupts with ifr set to PMU_INT_SNDBRT.
526 	 * Not processing them prevents us from seeing the adb devices
527 	 * afterwards, so we have to expect it unless we know the adb
528 	 * bus is empty.
529 	 */
530 	if (command == PMU_RESET_ADB) {
531 		waitfor = PMU_INT_ADB_AUTO | PMU_INT_ADB;
532 		if (adbempty == 0)
533 			waitfor |= PMU_INT_SNDBRT;
534 	} else
535 		waitfor = PMU_INT_ALL;
536 
537 	pmdata.data[0] = (u_char)(command & 0xff);
538 	pmdata.data[1] = 0;
539 	/* if the command is LISTEN, copy ADB data to PM buffer */
540 	if ((command & 0xc) == 0x8) {
541 		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
542 			pmdata.data[2] = buffer[0];	/* number of data */
543 			for (i = 0; i < buffer[0]; i++)
544 				pmdata.data[3 + i] = buffer[1 + i];
545 		} else
546 			pmdata.data[2] = 0;
547 	} else
548 		pmdata.data[2] = 0;
549 
550 	if ((command & 0xc) != 0xc) {	/* if the command is not TALK */
551 		/* set up stuff for adb_pass_up */
552 		packet.data[0] = 1 + pmdata.data[2];
553 		packet.data[1] = command;
554 		for (i = 0; i < pmdata.data[2]; i++)
555 			packet.data[i+2] = pmdata.data[i+3];
556 		packet.saveBuf = adbBuffer;
557 		packet.compRout = adbCompRout;
558 		packet.compData = adbCompData;
559 		packet.cmd = command;
560 		packet.unsol = 0;
561 		packet.ack_only = 1;
562 		adb_polling = 1;
563 		adb_pass_up(&packet);
564 		adb_polling = 0;
565 	}
566 
567 	rval = pmgrop(&pmdata);
568 	if (rval != 0) {
569 		splx(s);
570 		return 1;
571 	}
572 
573 	delay (1000);
574 
575 	adbWaiting = 1;
576 	adbWaitingCmd = command;
577 
578 	PM_VIA_INTR_ENABLE();
579 
580 	/* wait until the PM interrupt is occurred */
581 	ndelay = 0x8000;
582 #ifdef ADB_DEBUG
583 	oldifr = 0;
584 #endif
585 	while (adbWaiting == 1) {
586 		ifr = read_via_reg(VIA1, vIFR);
587 		if (ifr & waitfor) {
588 			pm_intr();
589 #ifdef PM_GRAB_SI
590 			(void)intr_dispatch(0x70);
591 #endif
592 #ifdef ADB_DEBUG
593 		} else if (ifr != oldifr) {
594 			if (adb_debug)
595 				printf("pm_adb_op: ignoring ifr %02x"
596 				    ", expecting %02x\n",
597 				    (u_int)ifr, (u_int)waitfor);
598 			oldifr = ifr;
599 #endif
600 		}
601 		if ((--ndelay) < 0) {
602 			splx(s);
603 			return 1;
604 		}
605 		delay(10);
606 	}
607 
608 	/* this command enables the interrupt by operating ADB devices */
609 	pmdata.command = 0x20;
610 	pmdata.num_data = 4;
611 	pmdata.s_buf = pmdata.data;
612 	pmdata.r_buf = pmdata.data;
613 	pmdata.data[0] = 0x00;
614 	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
615 	pmdata.data[2] = 0x00;
616 	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
617 	rval = pmgrop(&pmdata);
618 
619 	splx(s);
620 	return rval;
621 }
622 
623 
624 void
625 pm_adb_get_TALK_result(PMData *pmdata)
626 {
627 	int i;
628 	struct adbCommand packet;
629 
630 	/* set up data for adb_pass_up */
631 	packet.data[0] = pmdata->num_data-1;
632 	packet.data[1] = pmdata->data[3];
633 	for (i = 0; i <packet.data[0]-1; i++)
634 		packet.data[i+2] = pmdata->data[i+4];
635 
636 	packet.saveBuf = adbBuffer;
637 	packet.compRout = adbCompRout;
638 	packet.compData = adbCompData;
639 	packet.unsol = 0;
640 	packet.ack_only = 0;
641 	adb_polling = 1;
642 	adb_pass_up(&packet);
643 	adb_polling = 0;
644 
645 	adbWaiting = 0;
646 	adbBuffer = (long)0;
647 	adbCompRout = (long)0;
648 	adbCompData = (long)0;
649 }
650 
651 
652 void
653 pm_adb_get_ADB_data(PMData *pmdata)
654 {
655 	int i;
656 	struct adbCommand packet;
657 
658 	/* set up data for adb_pass_up */
659 	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
660 	packet.data[1] = pmdata->data[3];	/* ADB command */
661 	for (i = 0; i <packet.data[0]-1; i++)
662 		packet.data[i+2] = pmdata->data[i+4];
663 	packet.unsol = 1;
664 	packet.ack_only = 0;
665 	adb_pass_up(&packet);
666 }
667 
668 void
669 pm_adb_restart()
670 {
671 	PMData p;
672 
673 	p.command = PMU_RESET_CPU;
674 	p.num_data = 0;
675 	p.s_buf = p.data;
676 	p.r_buf = p.data;
677 	pmgrop(&p);
678 }
679 
680 void
681 pm_adb_poweroff()
682 {
683 	PMData p;
684 
685 	bzero(&p, sizeof p);
686 	p.command = PMU_POWER_OFF;
687 	p.num_data = 4;
688 	p.s_buf = p.data;
689 	p.r_buf = p.data;
690 	strlcpy(p.data, "MATT", sizeof p.data);
691 	pmgrop(&p);
692 }
693 
694 void
695 pm_read_date_time(time_t *time)
696 {
697 	PMData p;
698 
699 	p.command = PMU_READ_RTC;
700 	p.num_data = 0;
701 	p.s_buf = p.data;
702 	p.r_buf = p.data;
703 	pmgrop(&p);
704 
705 	bcopy(p.data, time, 4);
706 }
707 
708 void
709 pm_set_date_time(time_t time)
710 {
711 	PMData p;
712 
713 	p.command = PMU_SET_RTC;
714 	p.num_data = 4;
715 	p.s_buf = p.r_buf = p.data;
716 	bcopy(&time, p.data, 4);
717 	pmgrop(&p);
718 }
719 
720 #if 0
721 void
722 pm_eject_pcmcia(int slot)
723 {
724 	PMData p;
725 
726 	if (slot != 0 && slot != 1)
727 		return;
728 
729 	p.command = PMU_EJECT_PCMCIA;
730 	p.num_data = 1;
731 	p.s_buf = p.r_buf = p.data;
732 	p.data[0] = 5 + slot;	/* XXX */
733 	pmgrop(&p);
734 }
735 #endif
736 
737 
738 /*
739  * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
740  * for a clear description of the PMU results.
741  */
742 
743 int
744 pm_battery_info(int battery, struct pmu_battery_info *info)
745 {
746 	PMData p;
747 
748 	p.command = PMU_SMART_BATTERY_STATE;
749 	p.num_data = 1;
750 	p.s_buf = p.r_buf = p.data;
751 	p.data[0] = battery + 1;
752 	pmgrop(&p);
753 
754 	info->flags = p.data[1];
755 
756 	switch (p.data[0]) {
757 	case 3:
758 	case 4:
759 		info->cur_charge = p.data[2];
760 		info->max_charge = p.data[3];
761 		info->draw = *((signed char *)&p.data[4]);
762 		info->voltage = p.data[5];
763 		break;
764 	case 5:
765 		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
766 		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
767 		info->draw = *((signed short *)&p.data[6]);
768 		info->voltage = ((p.data[8] << 8) | (p.data[7]));
769 		break;
770 	default:
771 		/* XXX - Error condition */
772 		info->cur_charge = 0;
773 		info->max_charge = 0;
774 		info->draw = 0;
775 		info->voltage = 0;
776 		break;
777 	}
778 
779 	return 1;
780 }
781 
782 void
783 pmu_fileserver_mode(int on)
784 {
785 	PMData p;
786 
787 	p.command = PMU_POWER_EVENTS;
788 	p.num_data = 1;
789 	p.s_buf = p.r_buf = p.data;
790 	p.data[0] = PMU_PWR_GET_POWERUP_EVENTS;
791 	pmgrop(&p);
792 
793 	p.command = PMU_POWER_EVENTS;
794 	p.num_data = 3;
795 	p.s_buf = p.r_buf = p.data;
796 	p.data[1] = p.data[0];   /* result from the get */
797 	if (on) {
798 		p.data[0] = PMU_PWR_SET_POWERUP_EVENTS;
799 		p.data[2] = PMU_WAKE_AC_LOSS;
800 	} else {
801 		p.data[0] = PMU_PWR_CLR_POWERUP_EVENTS;
802 		p.data[2] = PMU_WAKE_AC_LOSS;
803 	}
804 	pmgrop(&p);
805 }
806