pm_direct.c revision 1.23
1/*	$NetBSD: pm_direct.c,v 1.23 2005/02/01 02:46:00 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.23 2005/02/01 02:46:00 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 *));
197void	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 *));
203void	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));
208void	pm_intr __P((void));
209int	pm_adb_op __P((u_char *, void *, void *, int));
210
211/* these functions also use the variables of adb_direct.c */
212void	pm_adb_get_TALK_result __P((PMData *));
213void	pm_adb_get_ADB_data __P((PMData *));
214void	pm_adb_poll_next_device_pm1 __P((PMData *));
215
216
217/*
218 * These variables are in adb_direct.c.
219 */
220extern u_char	*adbBuffer;	/* pointer to user data area */
221extern void	*adbCompRout;	/* pointer to the completion routine */
222extern void	*adbCompData;	/* pointer to the completion routine data */
223extern int	adbWaiting;	/* waiting for return data from the device */
224extern int	adbWaitingCmd;	/* ADB command we are waiting for */
225extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
226
227#define	ADB_MAX_MSG_LENGTH	16
228#define	ADB_MAX_HDR_LENGTH	8
229struct adbCommand {
230	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
231	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
232	u_char	*saveBuf;	/* where to save result */
233	u_char	*compRout;	/* completion routine pointer */
234	u_char	*compData;	/* completion routine data pointer */
235	u_int	cmd;		/* the original command for this data */
236	u_int	unsol;		/* 1 if packet was unsolicited */
237	u_int	ack_only;	/* 1 for no special processing */
238};
239extern	void	adb_pass_up __P((struct adbCommand *));
240
241#if 0
242/*
243 * Define the external functions
244 */
245extern int	zshard __P((int));		/* from zs.c */
246#endif
247
248#ifdef ADB_DEBUG
249/*
250 * This function dumps contents of the PMData
251 */
252void
253pm_printerr(ttl, rval, num, data)
254	char *ttl;
255	int rval;
256	int num;
257	char *data;
258{
259	int i;
260
261	printf("pm: %s:%04x %02x ", ttl, rval, num);
262	for (i = 0; i < num; i++)
263		printf("%02x ", data[i]);
264	printf("\n");
265}
266#endif
267
268
269
270/*
271 * Check the hardware type of the Power Manager
272 */
273void
274pm_setup_adb()
275{
276	pmHardware = PM_HW_PB5XX;	/* XXX */
277}
278
279static int
280strinlist(char *targ, char *list, int listlen)
281{
282	char	*str;
283	int	sl;
284
285	str = list;
286	while (listlen > 0) {
287		sl = strlen(str);
288		if (strncmp(targ, str, sl) == 0)
289			return 1;
290		str += sl+1;
291		listlen -= sl+1;
292	}
293	return 0;
294}
295
296/*
297 * Check the hardware type of the Power Manager
298 */
299void
300pm_init(void)
301{
302	uint32_t	regs[10];
303	PMData		pmdata;
304	char		compat[128];
305	int		clen, node, imask;
306
307	node = OF_peer(0);
308	if (node == -1) {
309		printf("pmu: Failed to get root");
310		return;
311	}
312	clen = OF_getprop(node, "compatible", compat, sizeof(compat));
313	if (clen <= 0) {
314		printf("pmu: failed to read root compatible data %d\n", clen);
315		return;
316	}
317
318	imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
319
320	if (strinlist("AAPL,3500", compat, clen) ||
321	    strinlist("AAPL,3400/2400", compat, clen)) {
322		/* How to distinguish BATT_COMET? */
323		pmu_nbatt = 1;
324		pmu_batt_type = BATT_HOOPER;
325		pmu_type = PMU_OHARE;
326	} else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
327		   strinlist("PowerBook1,1", compat, clen)) {
328		pmu_nbatt = 2;
329		pmu_batt_type = BATT_SMART;
330		pmu_type = PMU_G3;
331	} else {
332		pmu_nbatt = 1;
333		pmu_batt_type = BATT_SMART;
334		pmu_type = PMU_KEYLARGO;
335		node = getnodebyname(0, "power-mgt");
336		if (node == -1) {
337			printf("pmu: can't find power-mgt\n");
338			return;
339		}
340		clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
341		if (clen < 24) {
342			printf("pmu: failed to read prim-info\n");
343			return;
344		}
345		pmu_nbatt = regs[6] >> 16;
346	}
347
348	pmdata.command = PMU_SET_IMASK;
349	pmdata.num_data = 1;
350	pmdata.s_buf = pmdata.data;
351	pmdata.r_buf = pmdata.data;
352	pmdata.data[0] = imask;
353	pmgrop(&pmdata);
354}
355
356
357/*
358 * Check the existent ADB devices
359 */
360void
361pm_check_adb_devices(id)
362	int id;
363{
364	u_short ed = 0x1;
365
366	ed <<= id;
367	pm_existent_ADB_devices |= ed;
368}
369
370
371/*
372 * Wait until PM IC is busy
373 */
374int
375pm_wait_busy(delay)
376	int delay;
377{
378	while (PM_IS_ON) {
379#ifdef PM_GRAB_SI
380#if 0
381		zshard(0);		/* grab any serial interrupts */
382#else
383		(void)intr_dispatch(0x70);
384#endif
385#endif
386		if ((--delay) < 0)
387			return 1;	/* timeout */
388	}
389	return 0;
390}
391
392
393/*
394 * Wait until PM IC is free
395 */
396int
397pm_wait_free(delay)
398	int delay;
399{
400	while (PM_IS_OFF) {
401#ifdef PM_GRAB_SI
402#if 0
403		zshard(0);		/* grab any serial interrupts */
404#else
405		(void)intr_dispatch(0x70);
406#endif
407#endif
408		if ((--delay) < 0)
409			return 0;	/* timeout */
410	}
411	return 1;
412}
413
414
415
416/*
417 * Functions for the PB1XX series
418 */
419
420/*
421 * Receive data from PM for the PB1XX series
422 */
423int
424pm_receive_pm1(data)
425	u_char *data;
426{
427#if 0
428	int rval = 0xffffcd34;
429
430	via_reg(VIA2, vDirA) = 0x00;
431
432	switch (1) {
433	default:
434		if (pm_wait_busy(0x40) != 0)
435			break;			/* timeout */
436
437		PM_SET_STATE_ACKOFF();
438		*data = via_reg(VIA2, 0x200);
439
440		rval = 0xffffcd33;
441		if (pm_wait_free(0x40) == 0)
442			break;			/* timeout */
443
444		rval = 0x00;
445		break;
446	}
447
448	PM_SET_STATE_ACKON();
449	via_reg(VIA2, vDirA) = 0x00;
450
451	return rval;
452#else
453	panic("pm_receive_pm1");
454#endif
455}
456
457
458
459/*
460 * Send data to PM for the PB1XX series
461 */
462int
463pm_send_pm1(data, delay)
464	u_char data;
465	int delay;
466{
467#if 0
468	int rval;
469
470	via_reg(VIA2, vDirA) = 0xff;
471	via_reg(VIA2, 0x200) = data;
472
473	PM_SET_STATE_ACKOFF();
474	if (pm_wait_busy(0x400) != 0) {
475		PM_SET_STATE_ACKON();
476		via_reg(VIA2, vDirA) = 0x00;
477
478		return 0xffffcd36;
479	}
480
481	rval = 0x0;
482	PM_SET_STATE_ACKON();
483	if (pm_wait_free(0x40) == 0)
484		rval = 0xffffcd35;
485
486	PM_SET_STATE_ACKON();
487	via_reg(VIA2, vDirA) = 0x00;
488
489	return rval;
490#else
491	panic("pm_send_pm1");
492#endif
493}
494
495
496/*
497 * My PMgrOp routine for the PB1XX series
498 */
499int
500pm_pmgrop_pm1(pmdata)
501	PMData *pmdata;
502{
503#if 0
504	int i;
505	int s = 0x81815963;
506	u_char via1_vIER, via1_vDirA;
507	int rval = 0;
508	int num_pm_data = 0;
509	u_char pm_cmd;
510	u_char pm_data;
511	u_char *pm_buf;
512
513	/* disable all inetrrupts but PM */
514	via1_vIER = via_reg(VIA1, vIER);
515	PM_VIA_INTR_DISABLE();
516
517	via1_vDirA = via_reg(VIA1, vDirA);
518
519	switch (pmdata->command) {
520	default:
521		for (i = 0; i < 7; i++) {
522			via_reg(VIA2, vDirA) = 0x00;
523
524			/* wait until PM is free */
525			if (pm_wait_free(ADBDelay) == 0) {	/* timeout */
526				via_reg(VIA2, vDirA) = 0x00;
527				/* restore formar value */
528				via_reg(VIA1, vDirA) = via1_vDirA;
529				via_reg(VIA1, vIER) = via1_vIER;
530				return 0xffffcd38;
531			}
532
533			switch (mac68k_machine.machineid) {
534				case MACH_MACPB160:
535				case MACH_MACPB165:
536				case MACH_MACPB165C:
537				case MACH_MACPB180:
538				case MACH_MACPB180C:
539					{
540						int delay = ADBDelay * 16;
541
542						via_reg(VIA2, vDirA) = 0x00;
543						while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0))
544							delay--;
545
546						if (delay < 0) {	/* timeout */
547							via_reg(VIA2, vDirA) = 0x00;
548							/* restore formar value */
549							via_reg(VIA1, vIER) = via1_vIER;
550							return 0xffffcd38;
551						}
552					}
553			} /* end switch */
554
555			s = splhigh();
556
557			via1_vDirA = via_reg(VIA1, vDirA);
558			via_reg(VIA1, vDirA) &= 0x7f;
559
560			pm_cmd = (u_char)(pmdata->command & 0xff);
561			if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0)
562				break;	/* send command succeeded */
563
564			via_reg(VIA1, vDirA) = via1_vDirA;
565			splx(s);
566		} /* end for */
567
568		/* failed to send a command */
569		if (i == 7) {
570			via_reg(VIA2, vDirA) = 0x00;
571			/* restore formar value */
572			via_reg(VIA1, vDirA) = via1_vDirA;
573			via_reg(VIA1, vIER) = via1_vIER;
574			if (s != 0x81815963)
575				splx(s);
576			return 0xffffcd38;
577		}
578
579		/* send # of PM data */
580		num_pm_data = pmdata->num_data;
581		if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0)
582			break;			/* timeout */
583
584		/* send PM data */
585		pm_buf = (u_char *)pmdata->s_buf;
586		for (i = 0; i < num_pm_data; i++)
587			if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0)
588				break;		/* timeout */
589		if ((i != num_pm_data) && (num_pm_data != 0))
590			break;			/* timeout */
591
592		/* Will PM IC return data? */
593		if ((pm_cmd & 0x08) == 0) {
594			rval = 0;
595			break;			/* no returned data */
596		}
597
598		rval = 0xffffcd37;
599		if (pm_wait_busy(ADBDelay) != 0)
600			break;			/* timeout */
601
602		/* receive PM command */
603		if ((rval = pm_receive_pm1(&pm_data)) != 0)
604			break;
605
606		pmdata->command = pm_data;
607
608		/* receive number of PM data */
609		if ((rval = pm_receive_pm1(&pm_data)) != 0)
610			break;			/* timeout */
611		num_pm_data = pm_data;
612		pmdata->num_data = num_pm_data;
613
614		/* receive PM data */
615		pm_buf = (u_char *)pmdata->r_buf;
616		for (i = 0; i < num_pm_data; i++) {
617			if ((rval = pm_receive_pm1(&pm_data)) != 0)
618				break;		/* timeout */
619			pm_buf[i] = pm_data;
620		}
621
622		rval = 0;
623	}
624
625	via_reg(VIA2, vDirA) = 0x00;
626
627	/* restore formar value */
628	via_reg(VIA1, vDirA) = via1_vDirA;
629	via_reg(VIA1, vIER) = via1_vIER;
630	if (s != 0x81815963)
631		splx(s);
632
633	return rval;
634#else
635	panic("pm_pmgrop_pm1");
636#endif
637}
638
639
640/*
641 * My PM interrupt routine for PB1XX series
642 */
643void
644pm_intr_pm1()
645{
646#if 0
647	int s;
648	int rval;
649	PMData pmdata;
650
651	s = splhigh();
652
653	PM_VIA_CLR_INTR();				/* clear VIA1 interrupt */
654
655	/* ask PM what happend */
656	pmdata.command = PMU_INT_ACK;
657	pmdata.num_data = 0;
658	pmdata.data[0] = pmdata.data[1] = 0;
659	pmdata.s_buf = &pmdata.data[2];
660	pmdata.r_buf = &pmdata.data[2];
661	rval = pm_pmgrop_pm1(&pmdata);
662	if (rval != 0) {
663#ifdef ADB_DEBUG
664		if (adb_debug)
665			printf("pm: PM is not ready. error code=%08x\n", rval);
666#endif
667		splx(s);
668	}
669
670	if ((pmdata.data[2] & 0x10) == 0x10) {
671		if ((pmdata.data[2] & 0x0f) == 0) {
672			/* ADB data that were requested by TALK command */
673			pm_adb_get_TALK_result(&pmdata);
674		} else if ((pmdata.data[2] & 0x08) == 0x8) {
675			/* PM is requesting to poll  */
676			pm_adb_poll_next_device_pm1(&pmdata);
677		} else if ((pmdata.data[2] & 0x04) == 0x4) {
678			/* ADB device event */
679			pm_adb_get_ADB_data(&pmdata);
680		}
681	} else {
682#ifdef ADB_DEBUG
683		if (adb_debug)
684			pm_printerr("driver does not support this event.",
685			    rval, pmdata.num_data, pmdata.data);
686#endif
687	}
688
689	splx(s);
690#else
691	panic("pm_intr_pm1");
692#endif
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 */
916void
917pm_intr_pm2()
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;
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
996
997/*
998 * My PMgrOp routine
999 */
1000int
1001pmgrop(pmdata)
1002	PMData *pmdata;
1003{
1004	switch (pmHardware) {
1005	case PM_HW_PB1XX:
1006		return (pm_pmgrop_pm1(pmdata));
1007		break;
1008	case PM_HW_PB5XX:
1009		return (pm_pmgrop_pm2(pmdata));
1010		break;
1011	default:
1012		/* return (pmgrop_mrg(pmdata)); */
1013		return 1;
1014	}
1015}
1016
1017
1018/*
1019 * My PM interrupt routine
1020 */
1021void
1022pm_intr()
1023{
1024	switch (pmHardware) {
1025	case PM_HW_PB1XX:
1026		pm_intr_pm1();
1027		break;
1028	case PM_HW_PB5XX:
1029		pm_intr_pm2();
1030		break;
1031	default:
1032		break;
1033	}
1034}
1035
1036
1037
1038/*
1039 * Synchronous ADBOp routine for the Power Manager
1040 */
1041int
1042pm_adb_op(buffer, compRout, data, command)
1043	u_char *buffer;
1044	void *compRout;
1045	void *data;
1046	int command;
1047{
1048	int i;
1049	int s;
1050	int rval;
1051	int timo;
1052	PMData pmdata;
1053	struct adbCommand packet;
1054
1055	if (adbWaiting == 1)
1056		return 1;
1057
1058	s = splhigh();
1059	write_via_reg(VIA1, vIER, 0x10);
1060
1061 	adbBuffer = buffer;
1062	adbCompRout = compRout;
1063	adbCompData = data;
1064
1065	pmdata.command = PMU_ADB_CMD;
1066	pmdata.s_buf = pmdata.data;
1067	pmdata.r_buf = pmdata.data;
1068
1069	/* if the command is LISTEN, add number of ADB data to number of PM data */
1070	if ((command & 0xc) == 0x8) {
1071		if (buffer != (u_char *)0)
1072			pmdata.num_data = buffer[0] + 3;
1073	} else {
1074		pmdata.num_data = 3;
1075	}
1076
1077	pmdata.data[0] = (u_char)(command & 0xff);
1078	pmdata.data[1] = 0;
1079	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
1080		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
1081			pmdata.data[2] = buffer[0];		/* number of data */
1082			for (i = 0; i < buffer[0]; i++)
1083				pmdata.data[3 + i] = buffer[1 + i];
1084		} else
1085			pmdata.data[2] = 0;
1086	} else
1087		pmdata.data[2] = 0;
1088
1089	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
1090		/* set up stuff for adb_pass_up */
1091		packet.data[0] = 1 + pmdata.data[2];
1092		packet.data[1] = command;
1093		for (i = 0; i < pmdata.data[2]; i++)
1094			packet.data[i+2] = pmdata.data[i+3];
1095		packet.saveBuf = adbBuffer;
1096		packet.compRout = adbCompRout;
1097		packet.compData = adbCompData;
1098		packet.cmd = command;
1099		packet.unsol = 0;
1100		packet.ack_only = 1;
1101		adb_polling = 1;
1102		adb_pass_up(&packet);
1103		adb_polling = 0;
1104	}
1105
1106	rval = pmgrop(&pmdata);
1107	if (rval != 0) {
1108		splx(s);
1109		return 1;
1110	}
1111
1112	delay(10000);
1113
1114	adbWaiting = 1;
1115	adbWaitingCmd = command;
1116
1117	PM_VIA_INTR_ENABLE();
1118
1119	/* wait until the PM interrupt has occurred */
1120	timo = 0x80000;
1121	while (adbWaiting == 1) {
1122		if (read_via_reg(VIA1, vIFR) & 0x14)
1123			pm_intr();
1124#ifdef PM_GRAB_SI
1125#if 0
1126			zshard(0);		/* grab any serial interrupts */
1127#else
1128			(void)intr_dispatch(0x70);
1129#endif
1130#endif
1131		if ((--timo) < 0) {
1132			/* Try to take an interrupt anyway, just in case.
1133			 * This has been observed to happen on my ibook
1134			 * when i press a key after boot and before adb
1135			 * is attached;  For example, when booting with -d.
1136			 */
1137			pm_intr();
1138			if (adbWaiting) {
1139				printf("pm_adb_op: timeout. command = 0x%x\n",command);
1140				splx(s);
1141				return 1;
1142			}
1143#ifdef ADB_DEBUG
1144			else {
1145				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
1146			}
1147#endif
1148		}
1149	}
1150
1151	/* this command enables the interrupt by operating ADB devices */
1152	if (HwCfgFlags3 & 0x00020000) {		/* PB Duo series, PB 5XX series */
1153		pmdata.command = PMU_ADB_CMD;
1154		pmdata.num_data = 4;
1155		pmdata.s_buf = pmdata.data;
1156		pmdata.r_buf = pmdata.data;
1157		pmdata.data[0] = 0x00;
1158		pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
1159		pmdata.data[2] = 0x00;
1160		pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
1161	} else {				/* PB 1XX series */
1162		pmdata.command = PMU_ADB_CMD;
1163		pmdata.num_data = 3;
1164		pmdata.s_buf = pmdata.data;
1165		pmdata.r_buf = pmdata.data;
1166		pmdata.data[0] = (u_char)(command & 0xf0) | 0xc;
1167		pmdata.data[1] = 0x04;
1168		pmdata.data[2] = 0x00;
1169	}
1170	rval = pmgrop(&pmdata);
1171
1172	splx(s);
1173	return rval;
1174}
1175
1176
1177void
1178pm_adb_get_TALK_result(pmdata)
1179	PMData *pmdata;
1180{
1181	int i;
1182	struct adbCommand packet;
1183
1184	/* set up data for adb_pass_up */
1185	packet.data[0] = pmdata->num_data-1;
1186	packet.data[1] = pmdata->data[3];
1187	for (i = 0; i <packet.data[0]-1; i++)
1188		packet.data[i+2] = pmdata->data[i+4];
1189
1190	packet.saveBuf = adbBuffer;
1191	packet.compRout = adbCompRout;
1192	packet.compData = adbCompData;
1193	packet.unsol = 0;
1194	packet.ack_only = 0;
1195	adb_polling = 1;
1196	adb_pass_up(&packet);
1197	adb_polling = 0;
1198
1199	adbWaiting = 0;
1200	adbBuffer = (long)0;
1201	adbCompRout = (long)0;
1202	adbCompData = (long)0;
1203}
1204
1205
1206void
1207pm_adb_get_ADB_data(pmdata)
1208	PMData *pmdata;
1209{
1210	int i;
1211	struct adbCommand packet;
1212
1213	if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
1214	    pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
1215	    ((pmdata->data[2] & ~1) == 0xf4)) {
1216		if (pmdata->data[2] == 0xf4) {
1217			pm_eject_pcmcia(0);
1218		} else {
1219			pm_eject_pcmcia(1);
1220		}
1221		return;
1222	}
1223	/* set up data for adb_pass_up */
1224	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
1225	packet.data[1] = pmdata->data[3];	/* ADB command */
1226	for (i = 0; i <packet.data[0]-1; i++)
1227		packet.data[i+2] = pmdata->data[i+4];
1228	packet.unsol = 1;
1229	packet.ack_only = 0;
1230	adb_pass_up(&packet);
1231}
1232
1233
1234void
1235pm_adb_poll_next_device_pm1(pmdata)
1236	PMData *pmdata;
1237{
1238	int i;
1239	int ndid;
1240	u_short bendid = 0x1;
1241	int rval;
1242	PMData tmp_pmdata;
1243
1244	/* find another existent ADB device to poll */
1245	for (i = 1; i < 16; i++) {
1246		ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf;
1247		bendid <<= ndid;
1248		if ((pm_existent_ADB_devices & bendid) != 0)
1249			break;
1250	}
1251
1252	/* poll the other device */
1253	tmp_pmdata.command = PMU_ADB_CMD;
1254	tmp_pmdata.num_data = 3;
1255	tmp_pmdata.s_buf = tmp_pmdata.data;
1256	tmp_pmdata.r_buf = tmp_pmdata.data;
1257	tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc;
1258	tmp_pmdata.data[1] = 0x04;	/* magic spell for awaking the PM */
1259	tmp_pmdata.data[2] = 0x00;
1260	rval = pmgrop(&tmp_pmdata);
1261}
1262
1263void
1264pm_adb_restart()
1265{
1266	PMData p;
1267
1268	p.command = PMU_RESET_CPU;
1269	p.num_data = 0;
1270	p.s_buf = p.data;
1271	p.r_buf = p.data;
1272	pmgrop(&p);
1273}
1274
1275void
1276pm_adb_poweroff()
1277{
1278	PMData p;
1279
1280	p.command = PMU_POWER_OFF;
1281	p.num_data = 4;
1282	p.s_buf = p.data;
1283	p.r_buf = p.data;
1284	strcpy(p.data, "MATT");
1285	pmgrop(&p);
1286}
1287
1288void
1289pm_read_date_time(time)
1290	u_long *time;
1291{
1292	PMData p;
1293
1294	p.command = PMU_READ_RTC;
1295	p.num_data = 0;
1296	p.s_buf = p.data;
1297	p.r_buf = p.data;
1298	pmgrop(&p);
1299
1300	memcpy(time, p.data, 4);
1301}
1302
1303void
1304pm_set_date_time(time)
1305	u_long time;
1306{
1307	PMData p;
1308
1309	p.command = PMU_SET_RTC;
1310	p.num_data = 4;
1311	p.s_buf = p.r_buf = p.data;
1312	memcpy(p.data, &time, 4);
1313	pmgrop(&p);
1314}
1315
1316int
1317pm_read_brightness()
1318{
1319	PMData p;
1320
1321	p.command = PMU_READ_BRIGHTNESS;
1322	p.num_data = 1;		/* XXX why 1? */
1323	p.s_buf = p.r_buf = p.data;
1324	p.data[0] = 0;
1325	pmgrop(&p);
1326
1327	return p.data[0];
1328}
1329
1330void
1331pm_set_brightness(val)
1332	int val;
1333{
1334	PMData p;
1335
1336	val = 0x7f - val / 2;
1337	if (val < 0x08)
1338		val = 0x08;
1339	if (val > 0x78)
1340		val = 0x78;
1341
1342	p.command = PMU_SET_BRIGHTNESS;
1343	p.num_data = 1;
1344	p.s_buf = p.r_buf = p.data;
1345	p.data[0] = val;
1346	pmgrop(&p);
1347}
1348
1349void
1350pm_init_brightness()
1351{
1352	int val;
1353
1354	val = pm_read_brightness();
1355	pm_set_brightness(val);
1356}
1357
1358void
1359pm_eject_pcmcia(slot)
1360	int slot;
1361{
1362	PMData p;
1363
1364	if (slot != 0 && slot != 1)
1365		return;
1366
1367	p.command = PMU_EJECT_PCMCIA;
1368	p.num_data = 1;
1369	p.s_buf = p.r_buf = p.data;
1370	p.data[0] = 5 + slot;	/* XXX */
1371	pmgrop(&p);
1372}
1373
1374/*
1375 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
1376 * for a clear description of the PMU results.
1377 */
1378static int
1379pm_battery_info_smart(int battery, struct pmu_battery_info *info)
1380{
1381	PMData p;
1382
1383	p.command = PMU_SMART_BATTERY_STATE;
1384	p.num_data = 1;
1385	p.s_buf = p.r_buf = p.data;
1386	p.data[0] = battery + 1;
1387	pmgrop(&p);
1388
1389	info->flags = p.data[1];
1390
1391	info->secs_remaining = 0;
1392	switch (p.data[0]) {
1393	case 3:
1394	case 4:
1395		info->cur_charge = p.data[2];
1396		info->max_charge = p.data[3];
1397		info->draw = *((signed char *)&p.data[4]);
1398		info->voltage = p.data[5];
1399		break;
1400	case 5:
1401		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
1402		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
1403		info->draw = *((signed short *)&p.data[6]);
1404		info->voltage = ((p.data[8] << 8) | (p.data[7]));
1405		break;
1406	default:
1407		/* XXX - Error condition */
1408		info->cur_charge = 0;
1409		info->max_charge = 0;
1410		info->draw = 0;
1411		info->voltage = 0;
1412		break;
1413	}
1414	if (info->draw) {
1415		if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
1416			info->secs_remaining =
1417				((info->max_charge - info->cur_charge) * 3600)
1418				/ info->draw;
1419		} else {
1420			info->secs_remaining =
1421				(info->cur_charge * 3600) / -info->draw;
1422		}
1423	}
1424
1425	return 1;
1426}
1427
1428static int
1429pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
1430{
1431	PMData p;
1432	long pcharge=0, charge, vb, vmax, lmax;
1433	long vmax_charging, vmax_charged, amperage, voltage;
1434
1435	p.command = PMU_BATTERY_STATE;
1436	p.num_data = 0;
1437	p.s_buf = p.r_buf = p.data;
1438	pmgrop(&p);
1439
1440	info->flags = p.data[0];
1441
1442	if (info->flags & PMU_PWR_BATT_PRESENT) {
1443		if (ty == BATT_COMET) {
1444			vmax_charging = 213;
1445			vmax_charged = 189;
1446			lmax = 6500;
1447		} else {
1448			/* Experimental values */
1449			vmax_charging = 365;
1450			vmax_charged = 365;
1451			lmax = 6500;
1452		}
1453		vmax = vmax_charged;
1454		vb = (p.data[1] << 8) | p.data[2];
1455		voltage = (vb * 256 + 72665) / 10;
1456		amperage = (unsigned char) p.data[5];
1457		if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1458			if (amperage > 200)
1459				vb += ((amperage - 200) * 15)/100;
1460		} else if (info->flags & PMU_PWR_BATT_CHARGING) {
1461			vb = (vb * 97) / 100;
1462			vmax = vmax_charging;
1463		}
1464		charge = (100 * vb) / vmax;
1465		if (info->flags & PMU_PWR_PCHARGE_RESET) {
1466			pcharge = (p.data[6] << 8) | p.data[7];
1467			if (pcharge > lmax)
1468				pcharge = lmax;
1469			pcharge *= 100;
1470			pcharge = 100 - pcharge / lmax;
1471			if (pcharge < charge)
1472				charge = pcharge;
1473		}
1474		info->cur_charge = charge;
1475		info->max_charge = 100;
1476		info->draw = -amperage;
1477		info->voltage = voltage;
1478		if (amperage > 0)
1479			info->secs_remaining = (charge * 16440) / amperage;
1480		else
1481			info->secs_remaining = 0;
1482	} else {
1483		info->cur_charge = 0;
1484		info->max_charge = 0;
1485		info->draw = 0;
1486		info->voltage = 0;
1487		info->secs_remaining = 0;
1488	}
1489
1490	return 1;
1491}
1492
1493int
1494pm_battery_info(int battery, struct pmu_battery_info *info)
1495{
1496
1497	if (battery > pmu_nbatt)
1498		return 0;
1499
1500	switch (pmu_batt_type) {
1501	case BATT_COMET:
1502	case BATT_HOOPER:
1503		return pm_battery_info_legacy(battery, info, pmu_batt_type);
1504
1505	case BATT_SMART:
1506		return pm_battery_info_smart(battery, info);
1507	}
1508
1509	return 0;
1510}
1511
1512int
1513pm_read_nvram(addr)
1514	int addr;
1515{
1516	PMData p;
1517
1518	p.command = PMU_READ_NVRAM;
1519	p.num_data = 2;
1520	p.s_buf = p.r_buf = p.data;
1521	p.data[0] = addr >> 8;
1522	p.data[1] = addr;
1523	pmgrop(&p);
1524
1525	return p.data[0];
1526}
1527
1528void
1529pm_write_nvram(addr, val)
1530	int addr, val;
1531{
1532	PMData p;
1533
1534	p.command = PMU_WRITE_NVRAM;
1535	p.num_data = 3;
1536	p.s_buf = p.r_buf = p.data;
1537	p.data[0] = addr >> 8;
1538	p.data[1] = addr;
1539	p.data[2] = val;
1540	pmgrop(&p);
1541}
1542