xref: /netbsd/sys/arch/macppc/dev/pm_direct.c (revision d974db0a)
1 /*	$NetBSD: pm_direct.c,v 1.32 2007/10/17 19:55:19 garbled 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 /*
35  * TODO : Check bounds on PMData in pmgrop
36  *		callers should specify how much room for data is in the buffer
37  *		and that should be respected by the pmgrop
38  */
39 
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.32 2007/10/17 19:55:19 garbled Exp $");
42 
43 #ifdef DEBUG
44 #ifndef ADB_DEBUG
45 #define ADB_DEBUG
46 #endif
47 #endif
48 
49 /* #define	PM_GRAB_SI	1 */
50 
51 #include <sys/param.h>
52 #include <sys/device.h>
53 #include <sys/systm.h>
54 
55 #include <machine/adbsys.h>
56 #include <machine/autoconf.h>
57 #include <machine/cpu.h>
58 #include <machine/pio.h>
59 
60 #include <dev/ofw/openfirm.h>
61 
62 #include <macppc/dev/adbvar.h>
63 #include <macppc/dev/pm_direct.h>
64 #include <macppc/dev/viareg.h>
65 
66 extern int adb_polling;		/* Are we polling?  (Debugger mode) */
67 
68 /* hardware dependent values */
69 #define ADBDelay 100		/* XXX */
70 
71 /* useful macros */
72 #define PM_SR()			read_via_reg(VIA1, vSR)
73 #define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
74 #define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
75 #define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
76 
77 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
78 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
79 #define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
80 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
81 
82 /*
83  * Variables for internal use
84  */
85 u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
86 u_int	pm_LCD_brightness = 0x0;
87 u_int	pm_LCD_contrast = 0x0;
88 u_int	pm_counter = 0;			/* clock count */
89 
90 static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
91 static int	pmu_nbatt;
92 static int	strinlist(const char *, char *, int);
93 static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type;
94 
95 /* these values shows that number of data returned after 'send' cmd is sent */
96 signed char pm_send_cmd_type[] = {
97 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
98 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
99 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
100 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
101 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
102 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
103 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
104 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
105 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
106 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
107 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
108 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
109 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
110 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
111 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
112 	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
113 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
114 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
115 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
116 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
117 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
118 	0x01, 0x01, 0x03, 0x02,   -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 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
123 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
124 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
125 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
126 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
127 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
128 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
129 };
130 
131 /* these values shows that number of data returned after 'receive' cmd is sent */
132 signed char pm_receive_cmd_type[] = {
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 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
137 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
139 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
141 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
143 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
145 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
147 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
149 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
151 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
153 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
155 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
157 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
159 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
161 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
162 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
163 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
165 };
166 
167 
168 /*
169  * Define the private functions
170  */
171 
172 /* for debugging */
173 #ifdef ADB_DEBUG
174 void	pm_printerr __P((const char *, int, int, const char *));
175 #endif
176 
177 int	pm_wait_busy __P((int));
178 int	pm_wait_free __P((int));
179 
180 static int	pm_receive __P((u_char *));
181 static int	pm_send __P((u_char));
182 
183 /* these functions are called from adb_direct.c */
184 void	pm_setup_adb __P((void));
185 void	pm_check_adb_devices __P((int));
186 int	pm_adb_op __P((u_char *, adbComp *, volatile int *, int));
187 
188 /* these functions also use the variables of adb_direct.c */
189 void	pm_adb_get_TALK_result __P((PMData *));
190 void	pm_adb_get_ADB_data __P((PMData *));
191 
192 
193 /*
194  * These variables are in adb_direct.c.
195  */
196 extern u_char	*adbBuffer;	/* pointer to user data area */
197 extern adbComp	*adbCompRout;	/* pointer to the completion routine */
198 extern volatile int *adbCompData;	/* pointer to the completion routine data */
199 extern int	adbWaiting;	/* waiting for return data from the device */
200 extern int	adbWaitingCmd;	/* ADB command we are waiting for */
201 extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
202 
203 #define	ADB_MAX_MSG_LENGTH	16
204 #define	ADB_MAX_HDR_LENGTH	8
205 struct adbCommand {
206 	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
207 	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
208 	u_char	*saveBuf;	/* where to save result */
209 	adbComp	*compRout;	/* completion routine pointer */
210 	volatile int	*compData;	/* completion routine data pointer */
211 	u_int	cmd;		/* the original command for this data */
212 	u_int	unsol;		/* 1 if packet was unsolicited */
213 	u_int	ack_only;	/* 1 for no special processing */
214 };
215 extern	void	adb_pass_up __P((struct adbCommand *));
216 
217 #if 0
218 /*
219  * Define the external functions
220  */
221 extern int	zshard __P((int));		/* from zs.c */
222 #endif
223 
224 #ifdef ADB_DEBUG
225 /*
226  * This function dumps contents of the PMData
227  */
228 void
229 pm_printerr(ttl, rval, num, data)
230 	const char *ttl;
231 	int rval;
232 	int num;
233 	const char *data;
234 {
235 	int i;
236 
237 	printf("pm: %s:%04x %02x ", ttl, rval, num);
238 	for (i = 0; i < num; i++)
239 		printf("%02x ", data[i]);
240 	printf("\n");
241 }
242 #endif
243 
244 
245 
246 /*
247  * Check the hardware type of the Power Manager
248  */
249 void
250 pm_setup_adb()
251 {
252 }
253 
254 /*
255  * Search for targ in list.  list is an area of listlen bytes
256  * containing null-terminated strings.
257  */
258 static int
259 strinlist(const char *targ, char *list, int listlen)
260 {
261 	char	*str;
262 	int	sl;
263 	int	targlen;
264 
265 	str = list;
266 	targlen = strlen(targ);
267 	while (listlen > 0) {
268 		sl = strlen(str);
269 		if (sl == targlen && (strncmp(targ, str, sl) == 0))
270 			return 1;
271 		str += sl+1;
272 		listlen -= sl+1;
273 	}
274 	return 0;
275 }
276 
277 /*
278  * Check the hardware type of the Power Manager
279  */
280 void
281 pm_init(void)
282 {
283 	uint32_t	regs[10];
284 	PMData		pmdata;
285 	char		compat[128];
286 	int		clen, node, pm_imask;
287 
288 	node = OF_peer(0);
289 	if (node == -1) {
290 		printf("pmu: Failed to get root");
291 		return;
292 	}
293 	clen = OF_getprop(node, "compatible", compat, sizeof(compat));
294 	if (clen <= 0) {
295 		printf("pmu: failed to read root compatible data %d\n", clen);
296 		return;
297 	}
298 
299 	pm_imask =
300 	    PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
301 
302 	if (strinlist("AAPL,3500", compat, clen) ||
303 	    strinlist("AAPL,3400/2400", compat, clen)) {
304 		/* How to distinguish BATT_COMET? */
305 		pmu_nbatt = 1;
306 		pmu_batt_type = BATT_HOOPER;
307 		pmu_type = PMU_OHARE;
308 	} else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
309 		   strinlist("PowerBook1,1", compat, clen)) {
310 		pmu_nbatt = 2;
311 		pmu_batt_type = BATT_SMART;
312 		pmu_type = PMU_G3;
313 	} else {
314 		pmu_nbatt = 1;
315 		pmu_batt_type = BATT_SMART;
316 		pmu_type = PMU_KEYLARGO;
317 		node = getnodebyname(0, "power-mgt");
318 		if (node == -1) {
319 			printf("pmu: can't find power-mgt\n");
320 			return;
321 		}
322 		clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
323 		if (clen < 24) {
324 			printf("pmu: failed to read prim-info\n");
325 			return;
326 		}
327 		pmu_nbatt = regs[6] >> 16;
328 	}
329 
330 	pmdata.command = PMU_SET_IMASK;
331 	pmdata.num_data = 1;
332 	pmdata.s_buf = pmdata.data;
333 	pmdata.r_buf = pmdata.data;
334 	pmdata.data[0] = pm_imask;
335 	pmgrop(&pmdata);
336 }
337 
338 
339 /*
340  * Check the existent ADB devices
341  */
342 void
343 pm_check_adb_devices(id)
344 	int id;
345 {
346 	u_short ed = 0x1;
347 
348 	ed <<= id;
349 	pm_existent_ADB_devices |= ed;
350 }
351 
352 
353 /*
354  * Wait until PM IC is busy
355  */
356 int
357 pm_wait_busy(delaycycles)
358 	int delaycycles;
359 {
360 	while (PM_IS_ON) {
361 #ifdef PM_GRAB_SI
362 #if 0
363 		zshard(0);		/* grab any serial interrupts */
364 #else
365 		(void)intr_dispatch(0x70);
366 #endif
367 #endif
368 		if ((--delaycycles) < 0)
369 			return 1;	/* timeout */
370 	}
371 	return 0;
372 }
373 
374 
375 /*
376  * Wait until PM IC is free
377  */
378 int
379 pm_wait_free(delaycycles)
380 	int delaycycles;
381 {
382 	while (PM_IS_OFF) {
383 #ifdef PM_GRAB_SI
384 #if 0
385 		zshard(0);		/* grab any serial interrupts */
386 #else
387 		(void)intr_dispatch(0x70);
388 #endif
389 #endif
390 		if ((--delaycycles) < 0)
391 			return 0;	/* timeout */
392 	}
393 	return 1;
394 }
395 
396 
397 
398 /*
399  * Receive data from PMU
400  */
401 static int
402 pm_receive(data)
403 	u_char *data;
404 {
405 	int i;
406 	int rval;
407 
408 	rval = 0xffffcd34;
409 
410 	switch (1) {
411 	default:
412 		/* set VIA SR to input mode */
413 		via_reg_or(VIA1, vACR, 0x0c);
414 		via_reg_and(VIA1, vACR, ~0x10);
415 		i = PM_SR();
416 
417 		PM_SET_STATE_ACKOFF();
418 		if (pm_wait_busy((int)ADBDelay*32) != 0)
419 			break;		/* timeout */
420 
421 		PM_SET_STATE_ACKON();
422 		rval = 0xffffcd33;
423 		if (pm_wait_free((int)ADBDelay*32) == 0)
424 			break;		/* timeout */
425 
426 		*data = PM_SR();
427 		rval = 0;
428 
429 		break;
430 	}
431 
432 	PM_SET_STATE_ACKON();
433 	via_reg_or(VIA1, vACR, 0x1c);
434 
435 	return rval;
436 }
437 
438 
439 
440 /*
441  * Send data to PMU
442  */
443 static int
444 pm_send(data)
445 	u_char data;
446 {
447 	int rval;
448 
449 	via_reg_or(VIA1, vACR, 0x1c);
450 	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
451 
452 	PM_SET_STATE_ACKOFF();
453 	rval = 0xffffcd36;
454 	if (pm_wait_busy((int)ADBDelay*32) != 0) {
455 		PM_SET_STATE_ACKON();
456 
457 		via_reg_or(VIA1, vACR, 0x1c);
458 
459 		return rval;
460 	}
461 
462 	PM_SET_STATE_ACKON();
463 	rval = 0xffffcd35;
464 	if (pm_wait_free((int)ADBDelay*32) != 0)
465 		rval = 0;
466 
467 	PM_SET_STATE_ACKON();
468 	via_reg_or(VIA1, vACR, 0x1c);
469 
470 	return rval;
471 }
472 
473 
474 
475 /*
476  * The PMgrOp routine
477  */
478 int
479 pmgrop(pmdata)
480 	PMData *pmdata;
481 {
482 	int i;
483 	int s;
484 	u_char via1_vIER;
485 	int rval = 0;
486 	int num_pm_data = 0;
487 	u_char pm_cmd;
488 	short pm_num_rx_data;
489 	u_char pm_data;
490 	u_char *pm_buf;
491 
492 	s = splhigh();
493 
494 	/* disable all inetrrupts but PM */
495 	via1_vIER = 0x10;
496 	via1_vIER &= read_via_reg(VIA1, vIER);
497 	write_via_reg(VIA1, vIER, via1_vIER);
498 	if (via1_vIER != 0x0)
499 		via1_vIER |= 0x80;
500 
501 	switch (pmdata->command) {
502 	default:
503 		/* wait until PM is free */
504 		pm_cmd = (u_char)(pmdata->command & 0xff);
505 		rval = 0xcd38;
506 		if (pm_wait_free(ADBDelay * 4) == 0)
507 			break;			/* timeout */
508 
509 		/* send PM command */
510 		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
511 			break;				/* timeout */
512 
513 		/* send number of PM data */
514 		num_pm_data = pmdata->num_data;
515 		if (pm_send_cmd_type[pm_cmd] < 0) {
516 			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
517 				break;		/* timeout */
518 			pmdata->command = 0;
519 		}
520 		/* send PM data */
521 		pm_buf = (u_char *)pmdata->s_buf;
522 		for (i = 0 ; i < num_pm_data; i++)
523 			if ((rval = pm_send(pm_buf[i])) != 0)
524 				break;			/* timeout */
525 		if (i != num_pm_data)
526 			break;				/* timeout */
527 
528 
529 		/* check if PM will send me data  */
530 		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
531 		pmdata->num_data = pm_num_rx_data;
532 		if (pm_num_rx_data == 0) {
533 			rval = 0;
534 			break;				/* no return data */
535 		}
536 
537 		/* receive PM command */
538 		pm_data = pmdata->command;
539 		pm_num_rx_data--;
540 		if (pm_num_rx_data == 0)
541 			if ((rval = pm_receive(&pm_data)) != 0) {
542 				rval = 0xffffcd37;
543 				break;
544 			}
545 		pmdata->command = pm_data;
546 
547 		/* receive number of PM data */
548 		if (pm_num_rx_data < 0) {
549 			if ((rval = pm_receive(&pm_data)) != 0)
550 				break;		/* timeout */
551 			num_pm_data = pm_data;
552 		} else
553 			num_pm_data = pm_num_rx_data;
554 		pmdata->num_data = num_pm_data;
555 
556 		/* receive PM data */
557 		pm_buf = (u_char *)pmdata->r_buf;
558 		for (i = 0; i < num_pm_data; i++) {
559 			if ((rval = pm_receive(&pm_data)) != 0)
560 				break;			/* timeout */
561 			pm_buf[i] = pm_data;
562 		}
563 
564 		rval = 0;
565 	}
566 
567 	/* restore former value */
568 	write_via_reg(VIA1, vIER, via1_vIER);
569 	splx(s);
570 
571 	return rval;
572 }
573 
574 
575 /*
576  * My PMU interrupt routine
577  */
578 int
579 pm_intr(void *arg)
580 {
581 	int s;
582 	int rval;
583 	PMData pmdata;
584 
585 	s = splhigh();
586 
587 	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
588 						/* ask PM what happend */
589 	pmdata.command = PMU_INT_ACK;
590 	pmdata.num_data = 0;
591 	pmdata.s_buf = &pmdata.data[2];
592 	pmdata.r_buf = &pmdata.data[2];
593 	rval = pmgrop(&pmdata);
594 	if (rval != 0) {
595 #ifdef ADB_DEBUG
596 		if (adb_debug)
597 			printf("pm: PM is not ready. error code: %08x\n", rval);
598 #endif
599 		splx(s);
600 		return 0;
601 	}
602 
603 	switch ((u_int)(pmdata.data[2] & 0xff)) {
604 	case 0x00:		/* no event pending? */
605 		break;
606 	case 0x80:		/* 1 sec interrupt? */
607 		pm_counter++;
608 		break;
609 	case 0x08:		/* Brightness/Contrast button on LCD panel */
610 		/* get brightness and contrast of the LCD */
611 		pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
612 		pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
613 
614 		/* this is experimental code */
615 		pmdata.command = PMU_SET_BRIGHTNESS;
616 		pmdata.num_data = 1;
617 		pmdata.s_buf = pmdata.data;
618 		pmdata.r_buf = pmdata.data;
619 		pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
620 		if (pm_LCD_brightness < 0x08)
621 			pm_LCD_brightness = 0x08;
622 		if (pm_LCD_brightness > 0x78)
623 			pm_LCD_brightness = 0x78;
624 		pmdata.data[0] = pm_LCD_brightness;
625 		rval = pmgrop(&pmdata);
626 		break;
627 
628 	case 0x10:		/* ADB data requested by TALK command */
629 	case 0x14:
630 		pm_adb_get_TALK_result(&pmdata);
631 		break;
632 	case 0x16:		/* ADB device event */
633 	case 0x18:
634 	case 0x1e:
635 		pm_adb_get_ADB_data(&pmdata);
636 		break;
637 	default:
638 #ifdef ADB_DEBUG
639 		if (adb_debug)
640 			pm_printerr("driver does not support this event.",
641 			    pmdata.data[2], pmdata.num_data,
642 			    pmdata.data);
643 #endif
644 		break;
645 	}
646 
647 	splx(s);
648 
649 	return 1;
650 }
651 
652 
653 /*
654  * Synchronous ADBOp routine for the Power Manager
655  */
656 int
657 pm_adb_op(buffer, compRout, data, command)
658 	u_char *buffer;
659 	adbComp *compRout;
660 	volatile int *data;
661 	int command;
662 {
663 	int i;
664 	int s;
665 	int rval;
666 	int timo;
667 	PMData pmdata;
668 	struct adbCommand packet;
669 
670 	if (adbWaiting == 1)
671 		return 1;
672 
673 	s = splhigh();
674 	write_via_reg(VIA1, vIER, 0x10);
675 
676  	adbBuffer = buffer;
677 	adbCompRout = compRout;
678 	adbCompData = data;
679 
680 	pmdata.command = PMU_ADB_CMD;
681 	pmdata.s_buf = pmdata.data;
682 	pmdata.r_buf = pmdata.data;
683 
684 	/* if the command is LISTEN, add number of ADB data to number of PM data */
685 	if ((command & 0xc) == 0x8) {
686 		if (buffer != (u_char *)0)
687 			pmdata.num_data = buffer[0] + 3;
688 	} else {
689 		pmdata.num_data = 3;
690 	}
691 
692 	pmdata.data[0] = (u_char)(command & 0xff);
693 	pmdata.data[1] = 0;
694 	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
695 		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
696 			pmdata.data[2] = buffer[0];		/* number of data */
697 			for (i = 0; i < buffer[0]; i++)
698 				pmdata.data[3 + i] = buffer[1 + i];
699 		} else
700 			pmdata.data[2] = 0;
701 	} else
702 		pmdata.data[2] = 0;
703 
704 	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
705 		/* set up stuff for adb_pass_up */
706 		packet.data[0] = 1 + pmdata.data[2];
707 		packet.data[1] = command;
708 		for (i = 0; i < pmdata.data[2]; i++)
709 			packet.data[i+2] = pmdata.data[i+3];
710 		packet.saveBuf = adbBuffer;
711 		packet.compRout = adbCompRout;
712 		packet.compData = adbCompData;
713 		packet.cmd = command;
714 		packet.unsol = 0;
715 		packet.ack_only = 1;
716 		adb_polling = 1;
717 		adb_pass_up(&packet);
718 		adb_polling = 0;
719 	}
720 
721 	rval = pmgrop(&pmdata);
722 	if (rval != 0) {
723 		splx(s);
724 		return 1;
725 	}
726 
727 	delay(10000);
728 
729 	adbWaiting = 1;
730 	adbWaitingCmd = command;
731 
732 	PM_VIA_INTR_ENABLE();
733 
734 	/* wait until the PM interrupt has occurred */
735 	timo = 0x80000;
736 	while (adbWaiting == 1) {
737 		if (read_via_reg(VIA1, vIFR) & 0x14)
738 			pm_intr(NULL);
739 #ifdef PM_GRAB_SI
740 #if 0
741 			zshard(0);		/* grab any serial interrupts */
742 #else
743 			(void)intr_dispatch(0x70);
744 #endif
745 #endif
746 		if ((--timo) < 0) {
747 			/* Try to take an interrupt anyway, just in case.
748 			 * This has been observed to happen on my ibook
749 			 * when i press a key after boot and before adb
750 			 * is attached;  For example, when booting with -d.
751 			 */
752 			pm_intr(NULL);
753 			if (adbWaiting) {
754 				printf("pm_adb_op: timeout. command = 0x%x\n",command);
755 				splx(s);
756 				return 1;
757 			}
758 #ifdef ADB_DEBUG
759 			else {
760 				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
761 			}
762 #endif
763 		}
764 	}
765 
766 	/* this command enables the interrupt by operating ADB devices */
767 	pmdata.command = PMU_ADB_CMD;
768 	pmdata.num_data = 4;
769 	pmdata.s_buf = pmdata.data;
770 	pmdata.r_buf = pmdata.data;
771 	pmdata.data[0] = 0x00;
772 	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
773 	pmdata.data[2] = 0x00;
774 	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
775 	rval = pmgrop(&pmdata);
776 
777 	splx(s);
778 	return rval;
779 }
780 
781 
782 void
783 pm_adb_get_TALK_result(pmdata)
784 	PMData *pmdata;
785 {
786 	int i;
787 	struct adbCommand packet;
788 
789 	/* set up data for adb_pass_up */
790 	packet.data[0] = pmdata->num_data-1;
791 	packet.data[1] = pmdata->data[3];
792 	for (i = 0; i <packet.data[0]-1; i++)
793 		packet.data[i+2] = pmdata->data[i+4];
794 
795 	packet.saveBuf = adbBuffer;
796 	packet.compRout = adbCompRout;
797 	packet.compData = adbCompData;
798 	packet.unsol = 0;
799 	packet.ack_only = 0;
800 	adb_polling = 1;
801 	adb_pass_up(&packet);
802 	adb_polling = 0;
803 
804 	adbWaiting = 0;
805 	adbBuffer = (long)0;
806 	adbCompRout = (long)0;
807 	adbCompData = (long)0;
808 }
809 
810 
811 void
812 pm_adb_get_ADB_data(pmdata)
813 	PMData *pmdata;
814 {
815 	int i;
816 	struct adbCommand packet;
817 
818 	if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
819 	    pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
820 	    ((pmdata->data[2] & ~1) == 0xf4)) {
821 		if (pmdata->data[2] == 0xf4) {
822 			pm_eject_pcmcia(0);
823 		} else {
824 			pm_eject_pcmcia(1);
825 		}
826 		return;
827 	}
828 	/* set up data for adb_pass_up */
829 	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
830 	packet.data[1] = pmdata->data[3];	/* ADB command */
831 	for (i = 0; i <packet.data[0]-1; i++)
832 		packet.data[i+2] = pmdata->data[i+4];
833 	packet.unsol = 1;
834 	packet.ack_only = 0;
835 	adb_pass_up(&packet);
836 }
837 
838 
839 void
840 pm_adb_restart()
841 {
842 	PMData p;
843 
844 	p.command = PMU_RESET_CPU;
845 	p.num_data = 0;
846 	p.s_buf = p.data;
847 	p.r_buf = p.data;
848 	pmgrop(&p);
849 }
850 
851 void
852 pm_adb_poweroff()
853 {
854 	PMData p;
855 
856 	p.command = PMU_POWER_OFF;
857 	p.num_data = 4;
858 	p.s_buf = p.data;
859 	p.r_buf = p.data;
860 	strcpy(p.data, "MATT");
861 	pmgrop(&p);
862 }
863 
864 void
865 pm_read_date_time(t)
866 	u_long *t;
867 {
868 	PMData p;
869 
870 	p.command = PMU_READ_RTC;
871 	p.num_data = 0;
872 	p.s_buf = p.data;
873 	p.r_buf = p.data;
874 	pmgrop(&p);
875 
876 	memcpy(t, p.data, 4);
877 }
878 
879 void
880 pm_set_date_time(t)
881 	u_long t;
882 {
883 	PMData p;
884 
885 	p.command = PMU_SET_RTC;
886 	p.num_data = 4;
887 	p.s_buf = p.r_buf = p.data;
888 	memcpy(p.data, &t, 4);
889 	pmgrop(&p);
890 }
891 
892 int
893 pm_read_brightness()
894 {
895 	PMData p;
896 
897 	p.command = PMU_READ_BRIGHTNESS;
898 	p.num_data = 1;		/* XXX why 1? */
899 	p.s_buf = p.r_buf = p.data;
900 	p.data[0] = 0;
901 	pmgrop(&p);
902 
903 	return p.data[0];
904 }
905 
906 void
907 pm_set_brightness(val)
908 	int val;
909 {
910 	PMData p;
911 
912 	val = 0x7f - val / 2;
913 	if (val < 0x08)
914 		val = 0x08;
915 	if (val > 0x78)
916 		val = 0x78;
917 
918 	p.command = PMU_SET_BRIGHTNESS;
919 	p.num_data = 1;
920 	p.s_buf = p.r_buf = p.data;
921 	p.data[0] = val;
922 	pmgrop(&p);
923 }
924 
925 void
926 pm_init_brightness()
927 {
928 	int val;
929 
930 	val = pm_read_brightness();
931 	pm_set_brightness(val);
932 }
933 
934 void
935 pm_eject_pcmcia(slot)
936 	int slot;
937 {
938 	PMData p;
939 
940 	if (slot != 0 && slot != 1)
941 		return;
942 
943 	p.command = PMU_EJECT_PCMCIA;
944 	p.num_data = 1;
945 	p.s_buf = p.r_buf = p.data;
946 	p.data[0] = 5 + slot;	/* XXX */
947 	pmgrop(&p);
948 }
949 
950 /*
951  * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
952  * for a clear description of the PMU results.
953  */
954 static int
955 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
956 {
957 	PMData p;
958 
959 	p.command = PMU_SMART_BATTERY_STATE;
960 	p.num_data = 1;
961 	p.s_buf = p.r_buf = p.data;
962 	p.data[0] = battery + 1;
963 	pmgrop(&p);
964 
965 	info->flags = p.data[1];
966 
967 	info->secs_remaining = 0;
968 	switch (p.data[0]) {
969 	case 3:
970 	case 4:
971 		info->cur_charge = p.data[2];
972 		info->max_charge = p.data[3];
973 		info->draw = *((signed char *)&p.data[4]);
974 		info->voltage = p.data[5];
975 		break;
976 	case 5:
977 		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
978 		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
979 		info->draw = *((signed short *)&p.data[6]);
980 		info->voltage = ((p.data[8] << 8) | (p.data[7]));
981 		break;
982 	default:
983 		/* XXX - Error condition */
984 		info->cur_charge = 0;
985 		info->max_charge = 0;
986 		info->draw = 0;
987 		info->voltage = 0;
988 		break;
989 	}
990 	if (info->draw) {
991 		if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
992 			info->secs_remaining =
993 				((info->max_charge - info->cur_charge) * 3600)
994 				/ info->draw;
995 		} else {
996 			info->secs_remaining =
997 				(info->cur_charge * 3600) / -info->draw;
998 		}
999 	}
1000 
1001 	return 1;
1002 }
1003 
1004 static int
1005 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
1006 {
1007 	PMData p;
1008 	long pcharge=0, charge, vb, vmax, chargemax;
1009 	long vmax_charging, vmax_charged, amperage, voltage;
1010 
1011 	p.command = PMU_BATTERY_STATE;
1012 	p.num_data = 0;
1013 	p.s_buf = p.r_buf = p.data;
1014 	pmgrop(&p);
1015 
1016 	info->flags = p.data[0];
1017 
1018 	if (info->flags & PMU_PWR_BATT_PRESENT) {
1019 		if (ty == BATT_COMET) {
1020 			vmax_charging = 213;
1021 			vmax_charged = 189;
1022 			chargemax = 6500;
1023 		} else {
1024 			/* Experimental values */
1025 			vmax_charging = 365;
1026 			vmax_charged = 365;
1027 			chargemax = 6500;
1028 		}
1029 		vmax = vmax_charged;
1030 		vb = (p.data[1] << 8) | p.data[2];
1031 		voltage = (vb * 256 + 72665) / 10;
1032 		amperage = (unsigned char) p.data[5];
1033 		if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1034 			if (amperage > 200)
1035 				vb += ((amperage - 200) * 15)/100;
1036 		} else if (info->flags & PMU_PWR_BATT_CHARGING) {
1037 			vb = (vb * 97) / 100;
1038 			vmax = vmax_charging;
1039 		}
1040 		charge = (100 * vb) / vmax;
1041 		if (info->flags & PMU_PWR_PCHARGE_RESET) {
1042 			pcharge = (p.data[6] << 8) | p.data[7];
1043 			if (pcharge > chargemax)
1044 				pcharge = chargemax;
1045 			pcharge *= 100;
1046 			pcharge = 100 - pcharge / chargemax;
1047 			if (pcharge < charge)
1048 				charge = pcharge;
1049 		}
1050 		info->cur_charge = charge;
1051 		info->max_charge = 100;
1052 		info->draw = -amperage;
1053 		info->voltage = voltage;
1054 		if (amperage > 0)
1055 			info->secs_remaining = (charge * 16440) / amperage;
1056 		else
1057 			info->secs_remaining = 0;
1058 	} else {
1059 		info->cur_charge = 0;
1060 		info->max_charge = 0;
1061 		info->draw = 0;
1062 		info->voltage = 0;
1063 		info->secs_remaining = 0;
1064 	}
1065 
1066 	return 1;
1067 }
1068 
1069 int
1070 pm_battery_info(int battery, struct pmu_battery_info *info)
1071 {
1072 
1073 	if (battery > pmu_nbatt)
1074 		return 0;
1075 
1076 	switch (pmu_batt_type) {
1077 	case BATT_COMET:
1078 	case BATT_HOOPER:
1079 		return pm_battery_info_legacy(battery, info, pmu_batt_type);
1080 
1081 	case BATT_SMART:
1082 		return pm_battery_info_smart(battery, info);
1083 	}
1084 
1085 	return 0;
1086 }
1087 
1088 int
1089 pm_read_nvram(addr)
1090 	int addr;
1091 {
1092 	PMData p;
1093 
1094 	p.command = PMU_READ_NVRAM;
1095 	p.num_data = 2;
1096 	p.s_buf = p.r_buf = p.data;
1097 	p.data[0] = addr >> 8;
1098 	p.data[1] = addr;
1099 	pmgrop(&p);
1100 
1101 	return p.data[0];
1102 }
1103 
1104 void
1105 pm_write_nvram(addr, val)
1106 	int addr, val;
1107 {
1108 	PMData p;
1109 
1110 	p.command = PMU_WRITE_NVRAM;
1111 	p.num_data = 3;
1112 	p.s_buf = p.r_buf = p.data;
1113 	p.data[0] = addr >> 8;
1114 	p.data[1] = addr;
1115 	p.data[2] = val;
1116 	pmgrop(&p);
1117 }
1118