pm_direct.c revision 1.29
1/*	$NetBSD: pm_direct.c,v 1.29 2005/12/11 12:18:03 christos 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.29 2005/12/11 12:18:03 christos 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
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 */
85u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
86u_int	pm_LCD_brightness = 0x0;
87u_int	pm_LCD_contrast = 0x0;
88u_int	pm_counter = 0;			/* clock count */
89
90static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
91static int	pmu_nbatt;
92static int	strinlist(const char *, char *, int);
93static 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 */
96signed 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 */
132signed 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
174void	pm_printerr __P((const char *, int, int, const char *));
175#endif
176
177int	pm_wait_busy __P((int));
178int	pm_wait_free __P((int));
179
180static int	pm_receive __P((u_char *));
181static int	pm_send __P((u_char));
182
183/* these functions are called from adb_direct.c */
184void	pm_setup_adb __P((void));
185void	pm_check_adb_devices __P((int));
186int	pm_adb_op __P((u_char *, adbComp *, volatile int *, int));
187
188/* these functions also use the variables of adb_direct.c */
189void	pm_adb_get_TALK_result __P((PMData *));
190void	pm_adb_get_ADB_data __P((PMData *));
191
192
193/*
194 * These variables are in adb_direct.c.
195 */
196extern u_char	*adbBuffer;	/* pointer to user data area */
197extern adbComp	*adbCompRout;	/* pointer to the completion routine */
198extern volatile int *adbCompData;	/* pointer to the completion routine data */
199extern int	adbWaiting;	/* waiting for return data from the device */
200extern int	adbWaitingCmd;	/* ADB command we are waiting for */
201extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
202
203#define	ADB_MAX_MSG_LENGTH	16
204#define	ADB_MAX_HDR_LENGTH	8
205struct 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};
215extern	void	adb_pass_up __P((struct adbCommand *));
216
217#if 0
218/*
219 * Define the external functions
220 */
221extern int	zshard __P((int));		/* from zs.c */
222#endif
223
224#ifdef ADB_DEBUG
225/*
226 * This function dumps contents of the PMData
227 */
228void
229pm_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 */
249void
250pm_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 */
258static int
259strinlist(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 */
280void
281pm_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 */
342void
343pm_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 */
356int
357pm_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 */
378int
379pm_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 */
401static int
402pm_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 */
443static int
444pm_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 */
478int
479pmgrop(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 */
578int
579pm_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 */
656int
657pm_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
782void
783pm_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
811void
812pm_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
839void
840pm_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
851void
852pm_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
864void
865pm_read_date_time(time)
866	u_long *time;
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(time, p.data, 4);
877}
878
879void
880pm_set_date_time(time)
881	u_long time;
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, &time, 4);
889	pmgrop(&p);
890}
891
892int
893pm_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
906void
907pm_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
925void
926pm_init_brightness()
927{
928	int val;
929
930	val = pm_read_brightness();
931	pm_set_brightness(val);
932}
933
934void
935pm_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 */
954static int
955pm_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
1004static int
1005pm_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
1069int
1070pm_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
1088int
1089pm_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
1104void
1105pm_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