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