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