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