xref: /netbsd-src/sys/arch/macppc/dev/pm_direct.c (revision de1dfb1250df962f1ff3a011772cf58e605aed11)
1 /*	$NetBSD: pm_direct.c,v 1.20 2003/07/15 02:43:30 lukem Exp $	*/
2 
3 /*
4  * Copyright (C) 1997 Takashi Hamada
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *  This product includes software developed by Takashi Hamada
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.20 2003/07/15 02:43:30 lukem Exp $");
36 
37 #ifdef DEBUG
38 #ifndef ADB_DEBUG
39 #define ADB_DEBUG
40 #endif
41 #endif
42 
43 /* #define	PM_GRAB_SI	1 */
44 
45 #include <sys/param.h>
46 #include <sys/cdefs.h>
47 #include <sys/device.h>
48 #include <sys/systm.h>
49 
50 #include <machine/adbsys.h>
51 #include <machine/cpu.h>
52 
53 #include <macppc/dev/adbvar.h>
54 #include <macppc/dev/pm_direct.h>
55 #include <macppc/dev/viareg.h>
56 
57 extern int adb_polling;		/* Are we polling?  (Debugger mode) */
58 
59 /* hardware dependent values */
60 #define ADBDelay 100		/* XXX */
61 #define HwCfgFlags3 0x20000	/* XXX */
62 
63 /* define the types of the Power Manager */
64 #define PM_HW_UNKNOWN		0x00	/* don't know */
65 #define PM_HW_PB1XX		0x01	/* PowerBook 1XX series */
66 #define	PM_HW_PB5XX		0x02	/* PowerBook Duo and 5XX series */
67 
68 /* useful macros */
69 #define PM_SR()			read_via_reg(VIA1, vSR)
70 #define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
71 #define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
72 #define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
73 #if 0
74 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x04)
75 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x04)
76 #define PM_IS_ON		(0x02 == (read_via_reg(VIA2, vBufB) & 0x02))
77 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x02))
78 #else
79 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
80 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
81 #define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
82 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
83 #endif
84 
85 /*
86  * Variables for internal use
87  */
88 int	pmHardware = PM_HW_UNKNOWN;
89 u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
90 u_int	pm_LCD_brightness = 0x0;
91 u_int	pm_LCD_contrast = 0x0;
92 u_int	pm_counter = 0;			/* clock count */
93 
94 /* these values shows that number of data returned after 'send' cmd is sent */
95 signed char pm_send_cmd_type[] = {
96 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
97 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
98 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
99 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
100 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
101 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
102 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
103 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
104 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
105 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
106 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
107 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
108 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
109 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
110 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
111 	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
112 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
113 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
114 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
115 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
116 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
117 	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
118 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
119 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
120 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
121 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
122 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
123 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
124 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
125 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
126 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
127 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
128 };
129 
130 /* these values shows that number of data returned after 'receive' cmd is sent */
131 signed char pm_receive_cmd_type[] = {
132 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
134 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
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 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
140 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
142 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
144 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
146 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
148 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
150 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
152 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
154 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
156 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
158 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
160 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
161 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
162 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
164 };
165 
166 
167 /*
168  * Define the private functions
169  */
170 
171 /* for debugging */
172 #ifdef ADB_DEBUG
173 void	pm_printerr __P((char *, int, int, char *));
174 #endif
175 
176 int	pm_wait_busy __P((int));
177 int	pm_wait_free __P((int));
178 
179 /* these functions are for the PB1XX series */
180 int	pm_receive_pm1 __P((u_char *));
181 int	pm_send_pm1 __P((u_char,int));
182 int	pm_pmgrop_pm1 __P((PMData *));
183 void	pm_intr_pm1 __P((void));
184 
185 /* these functions are for the PB Duo series and the PB 5XX series */
186 int	pm_receive_pm2 __P((u_char *));
187 int	pm_send_pm2 __P((u_char));
188 int	pm_pmgrop_pm2 __P((PMData *));
189 void	pm_intr_pm2 __P((void));
190 
191 /* these functions are called from adb_direct.c */
192 void	pm_setup_adb __P((void));
193 void	pm_check_adb_devices __P((int));
194 void	pm_intr __P((void));
195 int	pm_adb_op __P((u_char *, void *, void *, int));
196 
197 /* these functions also use the variables of adb_direct.c */
198 void	pm_adb_get_TALK_result __P((PMData *));
199 void	pm_adb_get_ADB_data __P((PMData *));
200 void	pm_adb_poll_next_device_pm1 __P((PMData *));
201 
202 
203 /*
204  * These variables are in adb_direct.c.
205  */
206 extern u_char	*adbBuffer;	/* pointer to user data area */
207 extern void	*adbCompRout;	/* pointer to the completion routine */
208 extern void	*adbCompData;	/* pointer to the completion routine data */
209 extern int	adbWaiting;	/* waiting for return data from the device */
210 extern int	adbWaitingCmd;	/* ADB command we are waiting for */
211 extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
212 
213 #define	ADB_MAX_MSG_LENGTH	16
214 #define	ADB_MAX_HDR_LENGTH	8
215 struct adbCommand {
216 	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
217 	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
218 	u_char	*saveBuf;	/* where to save result */
219 	u_char	*compRout;	/* completion routine pointer */
220 	u_char	*compData;	/* completion routine data pointer */
221 	u_int	cmd;		/* the original command for this data */
222 	u_int	unsol;		/* 1 if packet was unsolicited */
223 	u_int	ack_only;	/* 1 for no special processing */
224 };
225 extern	void	adb_pass_up __P((struct adbCommand *));
226 
227 #if 0
228 /*
229  * Define the external functions
230  */
231 extern int	zshard __P((int));		/* from zs.c */
232 #endif
233 
234 #ifdef ADB_DEBUG
235 /*
236  * This function dumps contents of the PMData
237  */
238 void
239 pm_printerr(ttl, rval, num, data)
240 	char *ttl;
241 	int rval;
242 	int num;
243 	char *data;
244 {
245 	int i;
246 
247 	printf("pm: %s:%04x %02x ", ttl, rval, num);
248 	for (i = 0; i < num; i++)
249 		printf("%02x ", data[i]);
250 	printf("\n");
251 }
252 #endif
253 
254 
255 
256 /*
257  * Check the hardware type of the Power Manager
258  */
259 void
260 pm_setup_adb()
261 {
262 	pmHardware = PM_HW_PB5XX;	/* XXX */
263 }
264 
265 
266 /*
267  * Check the existent ADB devices
268  */
269 void
270 pm_check_adb_devices(id)
271 	int id;
272 {
273 	u_short ed = 0x1;
274 
275 	ed <<= id;
276 	pm_existent_ADB_devices |= ed;
277 }
278 
279 
280 /*
281  * Wait until PM IC is busy
282  */
283 int
284 pm_wait_busy(delay)
285 	int delay;
286 {
287 	while (PM_IS_ON) {
288 #ifdef PM_GRAB_SI
289 #if 0
290 		zshard(0);		/* grab any serial interrupts */
291 #else
292 		(void)intr_dispatch(0x70);
293 #endif
294 #endif
295 		if ((--delay) < 0)
296 			return 1;	/* timeout */
297 	}
298 	return 0;
299 }
300 
301 
302 /*
303  * Wait until PM IC is free
304  */
305 int
306 pm_wait_free(delay)
307 	int delay;
308 {
309 	while (PM_IS_OFF) {
310 #ifdef PM_GRAB_SI
311 #if 0
312 		zshard(0);		/* grab any serial interrupts */
313 #else
314 		(void)intr_dispatch(0x70);
315 #endif
316 #endif
317 		if ((--delay) < 0)
318 			return 0;	/* timeout */
319 	}
320 	return 1;
321 }
322 
323 
324 
325 /*
326  * Functions for the PB1XX series
327  */
328 
329 /*
330  * Receive data from PM for the PB1XX series
331  */
332 int
333 pm_receive_pm1(data)
334 	u_char *data;
335 {
336 #if 0
337 	int rval = 0xffffcd34;
338 
339 	via_reg(VIA2, vDirA) = 0x00;
340 
341 	switch (1) {
342 	default:
343 		if (pm_wait_busy(0x40) != 0)
344 			break;			/* timeout */
345 
346 		PM_SET_STATE_ACKOFF();
347 		*data = via_reg(VIA2, 0x200);
348 
349 		rval = 0xffffcd33;
350 		if (pm_wait_free(0x40) == 0)
351 			break;			/* timeout */
352 
353 		rval = 0x00;
354 		break;
355 	}
356 
357 	PM_SET_STATE_ACKON();
358 	via_reg(VIA2, vDirA) = 0x00;
359 
360 	return rval;
361 #else
362 	panic("pm_receive_pm1");
363 #endif
364 }
365 
366 
367 
368 /*
369  * Send data to PM for the PB1XX series
370  */
371 int
372 pm_send_pm1(data, delay)
373 	u_char data;
374 	int delay;
375 {
376 #if 0
377 	int rval;
378 
379 	via_reg(VIA2, vDirA) = 0xff;
380 	via_reg(VIA2, 0x200) = data;
381 
382 	PM_SET_STATE_ACKOFF();
383 	if (pm_wait_busy(0x400) != 0) {
384 		PM_SET_STATE_ACKON();
385 		via_reg(VIA2, vDirA) = 0x00;
386 
387 		return 0xffffcd36;
388 	}
389 
390 	rval = 0x0;
391 	PM_SET_STATE_ACKON();
392 	if (pm_wait_free(0x40) == 0)
393 		rval = 0xffffcd35;
394 
395 	PM_SET_STATE_ACKON();
396 	via_reg(VIA2, vDirA) = 0x00;
397 
398 	return rval;
399 #else
400 	panic("pm_send_pm1");
401 #endif
402 }
403 
404 
405 /*
406  * My PMgrOp routine for the PB1XX series
407  */
408 int
409 pm_pmgrop_pm1(pmdata)
410 	PMData *pmdata;
411 {
412 #if 0
413 	int i;
414 	int s = 0x81815963;
415 	u_char via1_vIER, via1_vDirA;
416 	int rval = 0;
417 	int num_pm_data = 0;
418 	u_char pm_cmd;
419 	u_char pm_data;
420 	u_char *pm_buf;
421 
422 	/* disable all inetrrupts but PM */
423 	via1_vIER = via_reg(VIA1, vIER);
424 	PM_VIA_INTR_DISABLE();
425 
426 	via1_vDirA = via_reg(VIA1, vDirA);
427 
428 	switch (pmdata->command) {
429 	default:
430 		for (i = 0; i < 7; i++) {
431 			via_reg(VIA2, vDirA) = 0x00;
432 
433 			/* wait until PM is free */
434 			if (pm_wait_free(ADBDelay) == 0) {	/* timeout */
435 				via_reg(VIA2, vDirA) = 0x00;
436 				/* restore formar value */
437 				via_reg(VIA1, vDirA) = via1_vDirA;
438 				via_reg(VIA1, vIER) = via1_vIER;
439 				return 0xffffcd38;
440 			}
441 
442 			switch (mac68k_machine.machineid) {
443 				case MACH_MACPB160:
444 				case MACH_MACPB165:
445 				case MACH_MACPB165C:
446 				case MACH_MACPB180:
447 				case MACH_MACPB180C:
448 					{
449 						int delay = ADBDelay * 16;
450 
451 						via_reg(VIA2, vDirA) = 0x00;
452 						while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0))
453 							delay--;
454 
455 						if (delay < 0) {	/* timeout */
456 							via_reg(VIA2, vDirA) = 0x00;
457 							/* restore formar value */
458 							via_reg(VIA1, vIER) = via1_vIER;
459 							return 0xffffcd38;
460 						}
461 					}
462 			} /* end switch */
463 
464 			s = splhigh();
465 
466 			via1_vDirA = via_reg(VIA1, vDirA);
467 			via_reg(VIA1, vDirA) &= 0x7f;
468 
469 			pm_cmd = (u_char)(pmdata->command & 0xff);
470 			if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0)
471 				break;	/* send command succeeded */
472 
473 			via_reg(VIA1, vDirA) = via1_vDirA;
474 			splx(s);
475 		} /* end for */
476 
477 		/* failed to send a command */
478 		if (i == 7) {
479 			via_reg(VIA2, vDirA) = 0x00;
480 			/* restore formar value */
481 			via_reg(VIA1, vDirA) = via1_vDirA;
482 			via_reg(VIA1, vIER) = via1_vIER;
483 			if (s != 0x81815963)
484 				splx(s);
485 			return 0xffffcd38;
486 		}
487 
488 		/* send # of PM data */
489 		num_pm_data = pmdata->num_data;
490 		if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0)
491 			break;			/* timeout */
492 
493 		/* send PM data */
494 		pm_buf = (u_char *)pmdata->s_buf;
495 		for (i = 0; i < num_pm_data; i++)
496 			if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0)
497 				break;		/* timeout */
498 		if ((i != num_pm_data) && (num_pm_data != 0))
499 			break;			/* timeout */
500 
501 		/* Will PM IC return data? */
502 		if ((pm_cmd & 0x08) == 0) {
503 			rval = 0;
504 			break;			/* no returned data */
505 		}
506 
507 		rval = 0xffffcd37;
508 		if (pm_wait_busy(ADBDelay) != 0)
509 			break;			/* timeout */
510 
511 		/* receive PM command */
512 		if ((rval = pm_receive_pm1(&pm_data)) != 0)
513 			break;
514 
515 		pmdata->command = pm_data;
516 
517 		/* receive number of PM data */
518 		if ((rval = pm_receive_pm1(&pm_data)) != 0)
519 			break;			/* timeout */
520 		num_pm_data = pm_data;
521 		pmdata->num_data = num_pm_data;
522 
523 		/* receive PM data */
524 		pm_buf = (u_char *)pmdata->r_buf;
525 		for (i = 0; i < num_pm_data; i++) {
526 			if ((rval = pm_receive_pm1(&pm_data)) != 0)
527 				break;		/* timeout */
528 			pm_buf[i] = pm_data;
529 		}
530 
531 		rval = 0;
532 	}
533 
534 	via_reg(VIA2, vDirA) = 0x00;
535 
536 	/* restore formar value */
537 	via_reg(VIA1, vDirA) = via1_vDirA;
538 	via_reg(VIA1, vIER) = via1_vIER;
539 	if (s != 0x81815963)
540 		splx(s);
541 
542 	return rval;
543 #else
544 	panic("pm_pmgrop_pm1");
545 #endif
546 }
547 
548 
549 /*
550  * My PM interrupt routine for PB1XX series
551  */
552 void
553 pm_intr_pm1()
554 {
555 #if 0
556 	int s;
557 	int rval;
558 	PMData pmdata;
559 
560 	s = splhigh();
561 
562 	PM_VIA_CLR_INTR();				/* clear VIA1 interrupt */
563 
564 	/* ask PM what happend */
565 	pmdata.command = 0x78;
566 	pmdata.num_data = 0;
567 	pmdata.data[0] = pmdata.data[1] = 0;
568 	pmdata.s_buf = &pmdata.data[2];
569 	pmdata.r_buf = &pmdata.data[2];
570 	rval = pm_pmgrop_pm1(&pmdata);
571 	if (rval != 0) {
572 #ifdef ADB_DEBUG
573 		if (adb_debug)
574 			printf("pm: PM is not ready. error code=%08x\n", rval);
575 #endif
576 		splx(s);
577 	}
578 
579 	if ((pmdata.data[2] & 0x10) == 0x10) {
580 		if ((pmdata.data[2] & 0x0f) == 0) {
581 			/* ADB data that were requested by TALK command */
582 			pm_adb_get_TALK_result(&pmdata);
583 		} else if ((pmdata.data[2] & 0x08) == 0x8) {
584 			/* PM is requesting to poll  */
585 			pm_adb_poll_next_device_pm1(&pmdata);
586 		} else if ((pmdata.data[2] & 0x04) == 0x4) {
587 			/* ADB device event */
588 			pm_adb_get_ADB_data(&pmdata);
589 		}
590 	} else {
591 #ifdef ADB_DEBUG
592 		if (adb_debug)
593 			pm_printerr("driver does not supported this event.",
594 			    rval, pmdata.num_data, pmdata.data);
595 #endif
596 	}
597 
598 	splx(s);
599 #else
600 	panic("pm_intr_pm1");
601 #endif
602 }
603 
604 
605 
606 /*
607  * Functions for the PB Duo series and the PB 5XX series
608  */
609 
610 /*
611  * Receive data from PM for the PB Duo series and the PB 5XX series
612  */
613 int
614 pm_receive_pm2(data)
615 	u_char *data;
616 {
617 	int i;
618 	int rval;
619 
620 	rval = 0xffffcd34;
621 
622 	switch (1) {
623 	default:
624 		/* set VIA SR to input mode */
625 		via_reg_or(VIA1, vACR, 0x0c);
626 		via_reg_and(VIA1, vACR, ~0x10);
627 		i = PM_SR();
628 
629 		PM_SET_STATE_ACKOFF();
630 		if (pm_wait_busy((int)ADBDelay*32) != 0)
631 			break;		/* timeout */
632 
633 		PM_SET_STATE_ACKON();
634 		rval = 0xffffcd33;
635 		if (pm_wait_free((int)ADBDelay*32) == 0)
636 			break;		/* timeout */
637 
638 		*data = PM_SR();
639 		rval = 0;
640 
641 		break;
642 	}
643 
644 	PM_SET_STATE_ACKON();
645 	via_reg_or(VIA1, vACR, 0x1c);
646 
647 	return rval;
648 }
649 
650 
651 
652 /*
653  * Send data to PM for the PB Duo series and the PB 5XX series
654  */
655 int
656 pm_send_pm2(data)
657 	u_char data;
658 {
659 	int rval;
660 
661 	via_reg_or(VIA1, vACR, 0x1c);
662 	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
663 
664 	PM_SET_STATE_ACKOFF();
665 	rval = 0xffffcd36;
666 	if (pm_wait_busy((int)ADBDelay*32) != 0) {
667 		PM_SET_STATE_ACKON();
668 
669 		via_reg_or(VIA1, vACR, 0x1c);
670 
671 		return rval;
672 	}
673 
674 	PM_SET_STATE_ACKON();
675 	rval = 0xffffcd35;
676 	if (pm_wait_free((int)ADBDelay*32) != 0)
677 		rval = 0;
678 
679 	PM_SET_STATE_ACKON();
680 	via_reg_or(VIA1, vACR, 0x1c);
681 
682 	return rval;
683 }
684 
685 
686 
687 /*
688  * My PMgrOp routine for the PB Duo series and the PB 5XX series
689  */
690 int
691 pm_pmgrop_pm2(pmdata)
692 	PMData *pmdata;
693 {
694 	int i;
695 	int s;
696 	u_char via1_vIER;
697 	int rval = 0;
698 	int num_pm_data = 0;
699 	u_char pm_cmd;
700 	short pm_num_rx_data;
701 	u_char pm_data;
702 	u_char *pm_buf;
703 
704 	s = splhigh();
705 
706 	/* disable all inetrrupts but PM */
707 	via1_vIER = 0x10;
708 	via1_vIER &= read_via_reg(VIA1, vIER);
709 	write_via_reg(VIA1, vIER, via1_vIER);
710 	if (via1_vIER != 0x0)
711 		via1_vIER |= 0x80;
712 
713 	switch (pmdata->command) {
714 	default:
715 		/* wait until PM is free */
716 		pm_cmd = (u_char)(pmdata->command & 0xff);
717 		rval = 0xcd38;
718 		if (pm_wait_free(ADBDelay * 4) == 0)
719 			break;			/* timeout */
720 
721 		if (HwCfgFlags3 & 0x00200000) {
722 			/* PB 160, PB 165(c), PB 180(c)? */
723 			int delay = ADBDelay * 16;
724 
725 			write_via_reg(VIA2, vDirA, 0x00);
726 			while ((read_via_reg(VIA2, 0x200) == 0x07) &&
727 			    (delay >= 0))
728 				delay--;
729 
730 			if (delay < 0) {
731 				rval = 0xffffcd38;
732 				break;		/* timeout */
733 			}
734 		}
735 
736 		/* send PM command */
737 		if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff))))
738 			break;				/* timeout */
739 
740 		/* send number of PM data */
741 		num_pm_data = pmdata->num_data;
742 		if (HwCfgFlags3 & 0x00020000) {		/* PB Duo, PB 5XX */
743 			if (pm_send_cmd_type[pm_cmd] < 0) {
744 				if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
745 					break;		/* timeout */
746 				pmdata->command = 0;
747 			}
748 		} else {				/* PB 1XX series ? */
749 			if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
750 				break;			/* timeout */
751 		}
752 		/* send PM data */
753 		pm_buf = (u_char *)pmdata->s_buf;
754 		for (i = 0 ; i < num_pm_data; i++)
755 			if ((rval = pm_send_pm2(pm_buf[i])) != 0)
756 				break;			/* timeout */
757 		if (i != num_pm_data)
758 			break;				/* timeout */
759 
760 
761 		/* check if PM will send me data  */
762 		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
763 		pmdata->num_data = pm_num_rx_data;
764 		if (pm_num_rx_data == 0) {
765 			rval = 0;
766 			break;				/* no return data */
767 		}
768 
769 		/* receive PM command */
770 		pm_data = pmdata->command;
771 		if (HwCfgFlags3 & 0x00020000) {		/* PB Duo, PB 5XX */
772 			pm_num_rx_data--;
773 			if (pm_num_rx_data == 0)
774 				if ((rval = pm_receive_pm2(&pm_data)) != 0) {
775 					rval = 0xffffcd37;
776 					break;
777 				}
778 			pmdata->command = pm_data;
779 		} else {				/* PB 1XX series ? */
780 			if ((rval = pm_receive_pm2(&pm_data)) != 0) {
781 				rval = 0xffffcd37;
782 				break;
783 			}
784 			pmdata->command = pm_data;
785 		}
786 
787 		/* receive number of PM data */
788 		if (HwCfgFlags3 & 0x00020000) {		/* PB Duo, PB 5XX */
789 			if (pm_num_rx_data < 0) {
790 				if ((rval = pm_receive_pm2(&pm_data)) != 0)
791 					break;		/* timeout */
792 				num_pm_data = pm_data;
793 			} else
794 				num_pm_data = pm_num_rx_data;
795 			pmdata->num_data = num_pm_data;
796 		} else {				/* PB 1XX serias ? */
797 			if ((rval = pm_receive_pm2(&pm_data)) != 0)
798 				break;			/* timeout */
799 			num_pm_data = pm_data;
800 			pmdata->num_data = num_pm_data;
801 		}
802 
803 		/* receive PM data */
804 		pm_buf = (u_char *)pmdata->r_buf;
805 		for (i = 0; i < num_pm_data; i++) {
806 			if ((rval = pm_receive_pm2(&pm_data)) != 0)
807 				break;			/* timeout */
808 			pm_buf[i] = pm_data;
809 		}
810 
811 		rval = 0;
812 	}
813 
814 	/* restore former value */
815 	write_via_reg(VIA1, vIER, via1_vIER);
816 	splx(s);
817 
818 	return rval;
819 }
820 
821 
822 /*
823  * My PM interrupt routine for the PB Duo series and the PB 5XX series
824  */
825 void
826 pm_intr_pm2()
827 {
828 	int s;
829 	int rval;
830 	PMData pmdata;
831 
832 	s = splhigh();
833 
834 	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
835 						/* ask PM what happend */
836 	pmdata.command = 0x78;
837 	pmdata.num_data = 0;
838 	pmdata.s_buf = &pmdata.data[2];
839 	pmdata.r_buf = &pmdata.data[2];
840 	rval = pm_pmgrop_pm2(&pmdata);
841 	if (rval != 0) {
842 #ifdef ADB_DEBUG
843 		if (adb_debug)
844 			printf("pm: PM is not ready. error code: %08x\n", rval);
845 #endif
846 		splx(s);
847 	}
848 
849 	switch ((u_int)(pmdata.data[2] & 0xff)) {
850 	case 0x00:		/* 1 sec interrupt? */
851 		break;
852 	case 0x80:		/* 1 sec interrupt? */
853 		pm_counter++;
854 		break;
855 	case 0x08:		/* Brightness/Contrast button on LCD panel */
856 		/* get brightness and contrast of the LCD */
857 		pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
858 		pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
859 /*
860 		pm_printerr("#08", rval, pmdata.num_data, pmdata.data);
861 		pmdata.command = 0x33;
862 		pmdata.num_data = 1;
863 		pmdata.s_buf = pmdata.data;
864 		pmdata.r_buf = pmdata.data;
865 		pmdata.data[0] = pm_LCD_contrast;
866 		rval = pm_pmgrop_pm2(&pmdata);
867 		pm_printerr("#33", rval, pmdata.num_data, pmdata.data);
868 */
869 		/* this is an experimental code */
870 		pmdata.command = 0x41;
871 		pmdata.num_data = 1;
872 		pmdata.s_buf = pmdata.data;
873 		pmdata.r_buf = pmdata.data;
874 		pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
875 		if (pm_LCD_brightness < 0x08)
876 			pm_LCD_brightness = 0x08;
877 		if (pm_LCD_brightness > 0x78)
878 			pm_LCD_brightness = 0x78;
879 		pmdata.data[0] = pm_LCD_brightness;
880 		rval = pm_pmgrop_pm2(&pmdata);
881 		break;
882 	case 0x10:		/* ADB data that were requested by TALK command */
883 	case 0x14:
884 		pm_adb_get_TALK_result(&pmdata);
885 		break;
886 	case 0x16:		/* ADB device event */
887 	case 0x18:
888 	case 0x1e:
889 		pm_adb_get_ADB_data(&pmdata);
890 		break;
891 	default:
892 #ifdef ADB_DEBUG
893 		if (adb_debug)
894 			pm_printerr("driver does not supported this event.",
895 			    pmdata.data[2], pmdata.num_data,
896 			    pmdata.data);
897 #endif
898 		break;
899 	}
900 
901 	splx(s);
902 }
903 
904 
905 /*
906  * My PMgrOp routine
907  */
908 int
909 pmgrop(pmdata)
910 	PMData *pmdata;
911 {
912 	switch (pmHardware) {
913 	case PM_HW_PB1XX:
914 		return (pm_pmgrop_pm1(pmdata));
915 		break;
916 	case PM_HW_PB5XX:
917 		return (pm_pmgrop_pm2(pmdata));
918 		break;
919 	default:
920 		/* return (pmgrop_mrg(pmdata)); */
921 		return 1;
922 	}
923 }
924 
925 
926 /*
927  * My PM interrupt routine
928  */
929 void
930 pm_intr()
931 {
932 	switch (pmHardware) {
933 	case PM_HW_PB1XX:
934 		pm_intr_pm1();
935 		break;
936 	case PM_HW_PB5XX:
937 		pm_intr_pm2();
938 		break;
939 	default:
940 		break;
941 	}
942 }
943 
944 
945 
946 /*
947  * Synchronous ADBOp routine for the Power Manager
948  */
949 int
950 pm_adb_op(buffer, compRout, data, command)
951 	u_char *buffer;
952 	void *compRout;
953 	void *data;
954 	int command;
955 {
956 	int i;
957 	int s;
958 	int rval;
959 	int timo;
960 	PMData pmdata;
961 	struct adbCommand packet;
962 
963 	if (adbWaiting == 1)
964 		return 1;
965 
966 	s = splhigh();
967 	write_via_reg(VIA1, vIER, 0x10);
968 
969  	adbBuffer = buffer;
970 	adbCompRout = compRout;
971 	adbCompData = data;
972 
973 	pmdata.command = 0x20;
974 	pmdata.s_buf = pmdata.data;
975 	pmdata.r_buf = pmdata.data;
976 
977 	/* if the command is LISTEN, add number of ADB data to number of PM data */
978 	if ((command & 0xc) == 0x8) {
979 		if (buffer != (u_char *)0)
980 			pmdata.num_data = buffer[0] + 3;
981 	} else {
982 		pmdata.num_data = 3;
983 	}
984 
985 	pmdata.data[0] = (u_char)(command & 0xff);
986 	pmdata.data[1] = 0;
987 	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
988 		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
989 			pmdata.data[2] = buffer[0];		/* number of data */
990 			for (i = 0; i < buffer[0]; i++)
991 				pmdata.data[3 + i] = buffer[1 + i];
992 		} else
993 			pmdata.data[2] = 0;
994 	} else
995 		pmdata.data[2] = 0;
996 
997 	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
998 		/* set up stuff for adb_pass_up */
999 		packet.data[0] = 1 + pmdata.data[2];
1000 		packet.data[1] = command;
1001 		for (i = 0; i < pmdata.data[2]; i++)
1002 			packet.data[i+2] = pmdata.data[i+3];
1003 		packet.saveBuf = adbBuffer;
1004 		packet.compRout = adbCompRout;
1005 		packet.compData = adbCompData;
1006 		packet.cmd = command;
1007 		packet.unsol = 0;
1008 		packet.ack_only = 1;
1009 		adb_polling = 1;
1010 		adb_pass_up(&packet);
1011 		adb_polling = 0;
1012 	}
1013 
1014 	rval = pmgrop(&pmdata);
1015 	if (rval != 0) {
1016 		splx(s);
1017 		return 1;
1018 	}
1019 
1020 	delay(10000);
1021 
1022 	adbWaiting = 1;
1023 	adbWaitingCmd = command;
1024 
1025 	PM_VIA_INTR_ENABLE();
1026 
1027 	/* wait until the PM interrupt has occurred */
1028 	timo = 0x80000;
1029 	while (adbWaiting == 1) {
1030 		if (read_via_reg(VIA1, vIFR) & 0x14)
1031 			pm_intr();
1032 #ifdef PM_GRAB_SI
1033 #if 0
1034 			zshard(0);		/* grab any serial interrupts */
1035 #else
1036 			(void)intr_dispatch(0x70);
1037 #endif
1038 #endif
1039 		if ((--timo) < 0) {
1040 			/* Try to take an interrupt anyway, just in case.
1041 			 * This has been observed to happen on my ibook
1042 			 * when i press a key after boot and before adb
1043 			 * is attached;  For example, when booting with -d.
1044 			 */
1045 			pm_intr();
1046 			if (adbWaiting) {
1047 				printf("pm_adb_op: timeout. command = 0x%x\n",command);
1048 				splx(s);
1049 				return 1;
1050 			}
1051 #ifdef ADB_DEBUG
1052 			else {
1053 				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
1054 			}
1055 #endif
1056 		}
1057 	}
1058 
1059 	/* this command enables the interrupt by operating ADB devices */
1060 	if (HwCfgFlags3 & 0x00020000) {		/* PB Duo series, PB 5XX series */
1061 		pmdata.command = 0x20;
1062 		pmdata.num_data = 4;
1063 		pmdata.s_buf = pmdata.data;
1064 		pmdata.r_buf = pmdata.data;
1065 		pmdata.data[0] = 0x00;
1066 		pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
1067 		pmdata.data[2] = 0x00;
1068 		pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
1069 	} else {				/* PB 1XX series */
1070 		pmdata.command = 0x20;
1071 		pmdata.num_data = 3;
1072 		pmdata.s_buf = pmdata.data;
1073 		pmdata.r_buf = pmdata.data;
1074 		pmdata.data[0] = (u_char)(command & 0xf0) | 0xc;
1075 		pmdata.data[1] = 0x04;
1076 		pmdata.data[2] = 0x00;
1077 	}
1078 	rval = pmgrop(&pmdata);
1079 
1080 	splx(s);
1081 	return rval;
1082 }
1083 
1084 
1085 void
1086 pm_adb_get_TALK_result(pmdata)
1087 	PMData *pmdata;
1088 {
1089 	int i;
1090 	struct adbCommand packet;
1091 
1092 	/* set up data for adb_pass_up */
1093 	packet.data[0] = pmdata->num_data-1;
1094 	packet.data[1] = pmdata->data[3];
1095 	for (i = 0; i <packet.data[0]-1; i++)
1096 		packet.data[i+2] = pmdata->data[i+4];
1097 
1098 	packet.saveBuf = adbBuffer;
1099 	packet.compRout = adbCompRout;
1100 	packet.compData = adbCompData;
1101 	packet.unsol = 0;
1102 	packet.ack_only = 0;
1103 	adb_polling = 1;
1104 	adb_pass_up(&packet);
1105 	adb_polling = 0;
1106 
1107 	adbWaiting = 0;
1108 	adbBuffer = (long)0;
1109 	adbCompRout = (long)0;
1110 	adbCompData = (long)0;
1111 }
1112 
1113 
1114 void
1115 pm_adb_get_ADB_data(pmdata)
1116 	PMData *pmdata;
1117 {
1118 	int i;
1119 	struct adbCommand packet;
1120 
1121 	/* set up data for adb_pass_up */
1122 	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
1123 	packet.data[1] = pmdata->data[3];	/* ADB command */
1124 	for (i = 0; i <packet.data[0]-1; i++)
1125 		packet.data[i+2] = pmdata->data[i+4];
1126 	packet.unsol = 1;
1127 	packet.ack_only = 0;
1128 	adb_pass_up(&packet);
1129 }
1130 
1131 
1132 void
1133 pm_adb_poll_next_device_pm1(pmdata)
1134 	PMData *pmdata;
1135 {
1136 	int i;
1137 	int ndid;
1138 	u_short bendid = 0x1;
1139 	int rval;
1140 	PMData tmp_pmdata;
1141 
1142 	/* find another existent ADB device to poll */
1143 	for (i = 1; i < 16; i++) {
1144 		ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf;
1145 		bendid <<= ndid;
1146 		if ((pm_existent_ADB_devices & bendid) != 0)
1147 			break;
1148 	}
1149 
1150 	/* poll the other device */
1151 	tmp_pmdata.command = 0x20;
1152 	tmp_pmdata.num_data = 3;
1153 	tmp_pmdata.s_buf = tmp_pmdata.data;
1154 	tmp_pmdata.r_buf = tmp_pmdata.data;
1155 	tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc;
1156 	tmp_pmdata.data[1] = 0x04;	/* magic spell for awaking the PM */
1157 	tmp_pmdata.data[2] = 0x00;
1158 	rval = pmgrop(&tmp_pmdata);
1159 }
1160 
1161 void
1162 pm_adb_restart()
1163 {
1164 	PMData p;
1165 
1166 	p.command = PMU_RESET_CPU;
1167 	p.num_data = 0;
1168 	p.s_buf = p.data;
1169 	p.r_buf = p.data;
1170 	pmgrop(&p);
1171 }
1172 
1173 void
1174 pm_adb_poweroff()
1175 {
1176 	PMData p;
1177 
1178 	p.command = PMU_POWER_OFF;
1179 	p.num_data = 4;
1180 	p.s_buf = p.data;
1181 	p.r_buf = p.data;
1182 	strcpy(p.data, "MATT");
1183 	pmgrop(&p);
1184 }
1185 
1186 void
1187 pm_read_date_time(time)
1188 	u_long *time;
1189 {
1190 	PMData p;
1191 
1192 	p.command = PMU_READ_RTC;
1193 	p.num_data = 0;
1194 	p.s_buf = p.data;
1195 	p.r_buf = p.data;
1196 	pmgrop(&p);
1197 
1198 	memcpy(time, p.data, 4);
1199 }
1200 
1201 void
1202 pm_set_date_time(time)
1203 	u_long time;
1204 {
1205 	PMData p;
1206 
1207 	p.command = PMU_SET_RTC;
1208 	p.num_data = 4;
1209 	p.s_buf = p.r_buf = p.data;
1210 	memcpy(p.data, &time, 4);
1211 	pmgrop(&p);
1212 }
1213 
1214 int
1215 pm_read_brightness()
1216 {
1217 	PMData p;
1218 
1219 	p.command = PMU_READ_BRIGHTNESS;
1220 	p.num_data = 1;		/* XXX why 1? */
1221 	p.s_buf = p.r_buf = p.data;
1222 	p.data[0] = 0;
1223 	pmgrop(&p);
1224 
1225 	return p.data[0];
1226 }
1227 
1228 void
1229 pm_set_brightness(val)
1230 	int val;
1231 {
1232 	PMData p;
1233 
1234 	val = 0x7f - val / 2;
1235 	if (val < 0x08)
1236 		val = 0x08;
1237 	if (val > 0x78)
1238 		val = 0x78;
1239 
1240 	p.command = PMU_SET_BRIGHTNESS;
1241 	p.num_data = 1;
1242 	p.s_buf = p.r_buf = p.data;
1243 	p.data[0] = val;
1244 	pmgrop(&p);
1245 }
1246 
1247 void
1248 pm_init_brightness()
1249 {
1250 	int val;
1251 
1252 	val = pm_read_brightness();
1253 	pm_set_brightness(val);
1254 }
1255 
1256 void
1257 pm_eject_pcmcia(slot)
1258 	int slot;
1259 {
1260 	PMData p;
1261 
1262 	if (slot != 0 && slot != 1)
1263 		return;
1264 
1265 	p.command = PMU_EJECT_PCMCIA;
1266 	p.num_data = 1;
1267 	p.s_buf = p.r_buf = p.data;
1268 	p.data[0] = 5 + slot;	/* XXX */
1269 	pmgrop(&p);
1270 }
1271 
1272 /*
1273  * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
1274  * for a clear description of the PMU results.
1275  */
1276 int
1277 pm_battery_info(int battery, struct pmu_battery_info *info)
1278 {
1279 	PMData p;
1280 
1281 	p.command = PMU_SMART_BATTERY_STATE;
1282 	p.num_data = 1;
1283 	p.s_buf = p.r_buf = p.data;
1284 	p.data[0] = battery + 1;
1285 	pmgrop(&p);
1286 
1287 	info->flags = p.data[1];
1288 
1289 	switch (p.data[0]) {
1290 	case 3:
1291 	case 4:
1292 		info->cur_charge = p.data[2];
1293 		info->max_charge = p.data[3];
1294 		info->draw = *((signed char *)&p.data[4]);
1295 		info->voltage = p.data[5];
1296 		break;
1297 	case 5:
1298 		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
1299 		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
1300 		info->draw = *((signed short *)&p.data[6]);
1301 		info->voltage = ((p.data[8] << 8) | (p.data[7]));
1302 		break;
1303 	default:
1304 		/* XXX - Error condition */
1305 		info->cur_charge = 0;
1306 		info->max_charge = 0;
1307 		info->draw = 0;
1308 		info->voltage = 0;
1309 		break;
1310 	}
1311 
1312 	return 1;
1313 }
1314 
1315 int
1316 pm_read_nvram(addr)
1317 	int addr;
1318 {
1319 	PMData p;
1320 
1321 	p.command = PMU_READ_NVRAM;
1322 	p.num_data = 2;
1323 	p.s_buf = p.r_buf = p.data;
1324 	p.data[0] = addr >> 8;
1325 	p.data[1] = addr;
1326 	pmgrop(&p);
1327 
1328 	return p.data[0];
1329 }
1330 
1331 void
1332 pm_write_nvram(addr, val)
1333 	int addr, val;
1334 {
1335 	PMData p;
1336 
1337 	p.command = PMU_WRITE_NVRAM;
1338 	p.num_data = 3;
1339 	p.s_buf = p.r_buf = p.data;
1340 	p.data[0] = addr >> 8;
1341 	p.data[1] = addr;
1342 	p.data[2] = val;
1343 	pmgrop(&p);
1344 }
1345