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