pm_direct.c revision 1.13
1/*	$OpenBSD: pm_direct.c,v 1.13 2003/10/16 03:54:48 deraadt Exp $	*/
2/*	$NetBSD: pm_direct.c,v 1.9 2000/06/08 22:10:46 tsubai Exp $	*/
3
4/*
5 * Copyright (C) 1997 Takashi Hamada
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *  This product includes software developed by Takashi Hamada
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#ifdef DEBUG
35#ifndef ADB_DEBUG
36#define ADB_DEBUG
37#endif
38#endif
39
40/* #define	PM_GRAB_SI	1 */
41
42#include <sys/param.h>
43#include <sys/cdefs.h>
44#include <sys/device.h>
45#include <sys/systm.h>
46
47#include <machine/adbsys.h>
48#include <machine/cpu.h>
49
50#include <macppc/dev/adbvar.h>
51#include <macppc/dev/pm_direct.h>
52#include <macppc/dev/viareg.h>
53
54/* hardware dependent values */
55#define ADBDelay 100		/* XXX */
56
57/* define the types of the Power Manager */
58#define PM_HW_UNKNOWN		0x00	/* don't know */
59#define	PM_HW_PB5XX		0x02	/* PowerBook Duo and 5XX series */
60
61/* useful macros */
62#define PM_SR()			read_via_reg(VIA1, vSR)
63#define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
64#define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
65#define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
66#if 0
67#define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x04)
68#define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x04)
69#define PM_IS_ON		(0x02 == (read_via_reg(VIA2, vBufB) & 0x02))
70#define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x02))
71#else
72#define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
73#define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
74#define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
75#define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
76#endif
77
78/*
79 * Variables for internal use
80 */
81int	pmHardware = PM_HW_UNKNOWN;
82u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
83u_int	pm_LCD_brightness = 0x0;
84u_int	pm_LCD_contrast = 0x0;
85
86/* these values shows that number of data returned after 'send' cmd is sent */
87signed char pm_send_cmd_type[] = {
88	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
89	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
90	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
91	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
92	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
93	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
94	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
95	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
96	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
97	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
98	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
99	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
100	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
101	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
102	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
103	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
104	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
105	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
106	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
107	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
108	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
109	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
110	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
111	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
112	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
113	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
114	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
115	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
116	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
117	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
118	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
119	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
120};
121
122/* these values shows that number of data returned after 'receive' cmd is sent */
123signed char pm_receive_cmd_type[] = {
124	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
126	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
128	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
130	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
132	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
134	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
136	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
138	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
140	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
142	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
144	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
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	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
150	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
152	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
153	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
154	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
156};
157
158
159/*
160 * Define the private functions
161 */
162
163/* for debugging */
164#ifdef ADB_DEBUG
165void	pm_printerr(char *, int, int, char *);
166#endif
167
168int	pm_wait_busy(int);
169int	pm_wait_free(int);
170
171/* these functions are for the PB Duo series and the PB 5XX series */
172int	pm_receive_pm2(u_char *);
173int	pm_send_pm2(u_char);
174int	pm_pmgrop_pm2(PMData *);
175void	pm_intr_pm2(void);
176
177/* this function is MRG-Based (for testing) */
178int	pm_pmgrop_mrg(PMData *);
179
180/* these functions also use the variables of adb_direct.c */
181void	pm_adb_get_TALK_result(PMData *);
182void	pm_adb_get_ADB_data(PMData *);
183
184
185/*
186 * These variables are in adb_direct.c.
187 */
188extern u_char	*adbBuffer;	/* pointer to user data area */
189extern void	*adbCompRout;	/* pointer to the completion routine */
190extern void	*adbCompData;	/* pointer to the completion routine data */
191extern int	adbWaiting;	/* waiting for return data from the device */
192extern int	adbWaitingCmd;	/* ADB command we are waiting for */
193extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
194
195#define	ADB_MAX_MSG_LENGTH	16
196#define	ADB_MAX_HDR_LENGTH	8
197struct adbCommand {
198	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
199	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
200	u_char	*saveBuf;	/* where to save result */
201	u_char	*compRout;	/* completion routine pointer */
202	u_char	*compData;	/* completion routine data pointer */
203	u_int	cmd;		/* the original command for this data */
204	u_int	unsol;		/* 1 if packet was unsolicited */
205	u_int	ack_only;	/* 1 for no special processing */
206};
207extern	void	adb_pass_up(struct adbCommand *);
208
209
210#ifdef ADB_DEBUG
211/*
212 * This function dumps contents of the PMData
213 */
214void
215pm_printerr(ttl, rval, num, data)
216	char *ttl;
217	int rval;
218	int num;
219	char *data;
220{
221	int i;
222
223	printf("pm: %s:%04x %02x ", ttl, rval, num);
224	for (i = 0; i < num; i++)
225		printf("%02x ", data[i]);
226	printf("\n");
227}
228#endif
229
230
231
232/*
233 * Check the hardware type of the Power Manager
234 */
235void
236pm_setup_adb()
237{
238	pmHardware = PM_HW_PB5XX;	/* XXX */
239}
240
241
242/*
243 * Check the existent ADB devices
244 */
245void
246pm_check_adb_devices(int id)
247{
248	u_short ed = 0x1;
249
250	ed <<= id;
251	pm_existent_ADB_devices |= ed;
252}
253
254
255/*
256 * Wait until PM IC is busy
257 */
258int
259pm_wait_busy(int delay)
260{
261	while (PM_IS_ON) {
262#ifdef PM_GRAB_SI
263		(void)intr_dispatch(0x70);
264#endif
265		if ((--delay) < 0)
266			return 1;	/* timeout */
267	}
268	return 0;
269}
270
271
272/*
273 * Wait until PM IC is free
274 */
275int
276pm_wait_free(int delay)
277{
278	while (PM_IS_OFF) {
279#ifdef PM_GRAB_SI
280		(void)intr_dispatch(0x70);
281#endif
282		if ((--delay) < 0)
283			return 0;	/* timeout */
284	}
285	return 1;
286}
287
288/*
289 * Functions for the PB Duo series and the PB 5XX series
290 */
291
292/*
293 * Receive data from PM for the PB Duo series and the PB 5XX series
294 */
295int
296pm_receive_pm2(u_char *data)
297{
298	int i;
299	int rval;
300
301	rval = 0xffffcd34;
302
303	switch (1) {
304	default:
305		/* set VIA SR to input mode */
306		via_reg_or(VIA1, vACR, 0x0c);
307		via_reg_and(VIA1, vACR, ~0x10);
308		i = PM_SR();
309
310		PM_SET_STATE_ACKOFF();
311		if (pm_wait_busy((int)ADBDelay*32) != 0)
312			break;		/* timeout */
313
314		PM_SET_STATE_ACKON();
315		rval = 0xffffcd33;
316		if (pm_wait_free((int)ADBDelay*32) == 0)
317			break;		/* timeout */
318
319		*data = PM_SR();
320		rval = 0;
321
322		break;
323	}
324
325	PM_SET_STATE_ACKON();
326	via_reg_or(VIA1, vACR, 0x1c);
327
328	return rval;
329}
330
331/*
332 * Send data to PM for the PB Duo series and the PB 5XX series
333 */
334int
335pm_send_pm2(data)
336	u_char data;
337{
338	int rval;
339
340	via_reg_or(VIA1, vACR, 0x1c);
341	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
342
343	PM_SET_STATE_ACKOFF();
344	rval = 0xffffcd36;
345	if (pm_wait_busy((int)ADBDelay*32) != 0) {
346		PM_SET_STATE_ACKON();
347		via_reg_or(VIA1, vACR, 0x1c);
348		return rval;
349	}
350
351	PM_SET_STATE_ACKON();
352	rval = 0xffffcd35;
353	if (pm_wait_free((int)ADBDelay*32) != 0)
354		rval = 0;
355
356	PM_SET_STATE_ACKON();
357	via_reg_or(VIA1, vACR, 0x1c);
358
359	return rval;
360}
361
362
363
364/*
365 * My PMgrOp routine for the PB Duo series and the PB 5XX series
366 */
367int
368pm_pmgrop_pm2(PMData *pmdata)
369{
370	int i;
371	int s;
372	u_char via1_vIER;
373	int rval = 0;
374	int num_pm_data = 0;
375	u_char pm_cmd;
376	short pm_num_rx_data;
377	u_char pm_data;
378	u_char *pm_buf;
379
380	s = splhigh();
381
382	/* disable all inetrrupts but PM */
383	via1_vIER = 0x10;
384	via1_vIER &= read_via_reg(VIA1, vIER);
385	write_via_reg(VIA1, vIER, via1_vIER);
386	if (via1_vIER != 0x0)
387		via1_vIER |= 0x80;
388
389	switch (pmdata->command) {
390	default:
391		/* wait until PM is free */
392		pm_cmd = (u_char)(pmdata->command & 0xff);
393		rval = 0xcd38;
394		if (pm_wait_free(ADBDelay * 4) == 0)
395			break;			/* timeout */
396
397		/* send PM command */
398		if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff))))
399			break;				/* timeout */
400
401		/* send number of PM data */
402		num_pm_data = pmdata->num_data;
403		if (pm_send_cmd_type[pm_cmd] < 0) {
404			if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
405				break;		/* timeout */
406			pmdata->command = 0;
407		}
408		/* send PM data */
409		pm_buf = (u_char *)pmdata->s_buf;
410		for (i = 0 ; i < num_pm_data; i++)
411			if ((rval = pm_send_pm2(pm_buf[i])) != 0)
412				break;			/* timeout */
413		if (i != num_pm_data)
414			break;				/* timeout */
415
416
417		/* check if PM will send me data  */
418		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
419		pmdata->num_data = pm_num_rx_data;
420		if (pm_num_rx_data == 0) {
421			rval = 0;
422			break;				/* no return data */
423		}
424
425		/* receive PM command */
426		pm_data = pmdata->command;
427		pm_num_rx_data--;
428		if (pm_num_rx_data == 0)
429			if ((rval = pm_receive_pm2(&pm_data)) != 0) {
430				rval = 0xffffcd37;
431				break;
432			}
433		pmdata->command = pm_data;
434
435		/* receive number of PM data */
436		if (pm_num_rx_data < 0) {
437			if ((rval = pm_receive_pm2(&pm_data)) != 0)
438				break;		/* timeout */
439			num_pm_data = pm_data;
440		} else
441			num_pm_data = pm_num_rx_data;
442		pmdata->num_data = num_pm_data;
443
444		/* receive PM data */
445		pm_buf = (u_char *)pmdata->r_buf;
446		for (i = 0; i < num_pm_data; i++) {
447			if ((rval = pm_receive_pm2(&pm_data)) != 0)
448				break;			/* timeout */
449			pm_buf[i] = pm_data;
450		}
451
452		rval = 0;
453	}
454
455	/* restore former value */
456	write_via_reg(VIA1, vIER, via1_vIER);
457	splx(s);
458
459	return rval;
460}
461
462
463/*
464 * My PM interrupt routine for the PB Duo series and the PB 5XX series
465 */
466void
467pm_intr_pm2()
468{
469	int s;
470	int rval;
471	PMData pmdata;
472
473	s = splhigh();
474
475	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
476						/* ask PM what happend */
477	pmdata.command = 0x78;
478	pmdata.num_data = 0;
479	pmdata.s_buf = &pmdata.data[2];
480	pmdata.r_buf = &pmdata.data[2];
481	rval = pm_pmgrop_pm2(&pmdata);
482	if (rval != 0) {
483#ifdef ADB_DEBUG
484		if (adb_debug)
485			printf("pm: PM is not ready. error code: %08x\n", rval);
486#endif
487		splx(s);
488		return;
489	}
490
491	switch ((u_int)(pmdata.data[2] & 0xff)) {
492	case 0x00:		/* 1 sec interrupt? */
493		break;
494	case PMU_INT_TICK:	/* 1 sec interrupt? */
495		break;
496	case PMU_INT_SNDBRT:	/* Brightness/Contrast button on LCD panel */
497		break;
498	case PMU_INT_ADB:	/* ADB data requested by TALK command */
499	case PMU_INT_ADB|PMU_INT_ADB_AUTO:
500		pm_adb_get_TALK_result(&pmdata);
501		break;
502	case 0x16:		/* ADB device event */
503	case 0x18:
504	case 0x1e:
505	case PMU_INT_WAKEUP:
506		pm_adb_get_ADB_data(&pmdata);
507		break;
508	default:
509#ifdef ADB_DEBUG
510		if (adb_debug)
511			pm_printerr("driver does not support this event.",
512			    pmdata.data[2], pmdata.num_data,
513			    pmdata.data);
514#endif
515		break;
516	}
517
518	splx(s);
519}
520
521
522/*
523 * My PMgrOp routine
524 */
525int
526pmgrop(PMData *pmdata)
527{
528	switch (pmHardware) {
529	case PM_HW_PB5XX:
530		return (pm_pmgrop_pm2(pmdata));
531	default:
532		/* return (pmgrop_mrg(pmdata)); */
533		return 1;
534	}
535}
536
537
538/*
539 * My PM interrupt routine
540 */
541void
542pm_intr()
543{
544	switch (pmHardware) {
545	case PM_HW_PB5XX:
546		pm_intr_pm2();
547		break;
548	default:
549		break;
550	}
551}
552
553
554
555/*
556 * Synchronous ADBOp routine for the Power Manager
557 */
558int
559pm_adb_op(u_char *buffer, void *compRout, void *data, int command)
560{
561	int i;
562	int s;
563	int rval;
564	int ndelay;
565	PMData pmdata;
566	struct adbCommand packet;
567
568	if (adbWaiting == 1)
569		return 1;
570
571	s = splhigh();
572	write_via_reg(VIA1, vIER, 0x10);
573
574 	adbBuffer = buffer;
575	adbCompRout = compRout;
576	adbCompData = data;
577
578	pmdata.command = 0x20;
579	pmdata.s_buf = pmdata.data;
580	pmdata.r_buf = pmdata.data;
581
582	/*
583	 * if the command is LISTEN,
584	 * add number of ADB data to number of PM data
585	 */
586	if ((command & 0xc) == 0x8) {
587		if (buffer != (u_char *)0)
588			pmdata.num_data = buffer[0] + 3;
589	} else
590		pmdata.num_data = 3;
591
592	pmdata.data[0] = (u_char)(command & 0xff);
593	pmdata.data[1] = 0;
594	/* if the command is LISTEN, copy ADB data to PM buffer */
595	if ((command & 0xc) == 0x8) {
596		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
597			pmdata.data[2] = buffer[0];	/* number of data */
598			for (i = 0; i < buffer[0]; i++)
599				pmdata.data[3 + i] = buffer[1 + i];
600		} else
601			pmdata.data[2] = 0;
602	} else
603		pmdata.data[2] = 0;
604
605	if ((command & 0xc) != 0xc) {	/* if the command is not TALK */
606		/* set up stuff for adb_pass_up */
607		packet.data[0] = 1 + pmdata.data[2];
608		packet.data[1] = command;
609		for (i = 0; i < pmdata.data[2]; i++)
610			packet.data[i+2] = pmdata.data[i+3];
611		packet.saveBuf = adbBuffer;
612		packet.compRout = adbCompRout;
613		packet.compData = adbCompData;
614		packet.cmd = command;
615		packet.unsol = 0;
616		packet.ack_only = 1;
617		adb_polling = 1;
618		adb_pass_up(&packet);
619		adb_polling = 0;
620	}
621
622	rval = pmgrop(&pmdata);
623	if (rval != 0) {
624		splx(s);
625		return 1;
626	}
627
628	delay (1000);
629
630	adbWaiting = 1;
631	adbWaitingCmd = command;
632
633	PM_VIA_INTR_ENABLE();
634
635	/* wait until the PM interrupt is occurred */
636	ndelay = 0x8000;
637	while (adbWaiting == 1) {
638		if (read_via_reg(VIA1, vIFR) != 0)
639			pm_intr();
640#ifdef PM_GRAB_SI
641			(void)intr_dispatch(0x70);
642#endif
643		if ((--ndelay) < 0) {
644			splx(s);
645			return 1;
646		}
647		delay(10);
648	}
649
650	/* this command enables the interrupt by operating ADB devices */
651	pmdata.command = 0x20;
652	pmdata.num_data = 4;
653	pmdata.s_buf = pmdata.data;
654	pmdata.r_buf = pmdata.data;
655	pmdata.data[0] = 0x00;
656	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
657	pmdata.data[2] = 0x00;
658	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
659	rval = pmgrop(&pmdata);
660
661	splx(s);
662	return rval;
663}
664
665
666void
667pm_adb_get_TALK_result(PMData *pmdata)
668{
669	int i;
670	struct adbCommand packet;
671
672	/* set up data for adb_pass_up */
673	packet.data[0] = pmdata->num_data-1;
674	packet.data[1] = pmdata->data[3];
675	for (i = 0; i <packet.data[0]-1; i++)
676		packet.data[i+2] = pmdata->data[i+4];
677
678	packet.saveBuf = adbBuffer;
679	packet.compRout = adbCompRout;
680	packet.compData = adbCompData;
681	packet.unsol = 0;
682	packet.ack_only = 0;
683	adb_polling = 1;
684	adb_pass_up(&packet);
685	adb_polling = 0;
686
687	adbWaiting = 0;
688	adbBuffer = (long)0;
689	adbCompRout = (long)0;
690	adbCompData = (long)0;
691}
692
693
694void
695pm_adb_get_ADB_data(PMData *pmdata)
696{
697	int i;
698	struct adbCommand packet;
699
700	/* set up data for adb_pass_up */
701	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
702	packet.data[1] = pmdata->data[3];	/* ADB command */
703	for (i = 0; i <packet.data[0]-1; i++)
704		packet.data[i+2] = pmdata->data[i+4];
705	packet.unsol = 1;
706	packet.ack_only = 0;
707	adb_pass_up(&packet);
708}
709
710void
711pm_adb_restart()
712{
713	PMData p;
714
715	p.command = PMU_RESET_CPU;
716	p.num_data = 0;
717	p.s_buf = p.data;
718	p.r_buf = p.data;
719	pmgrop(&p);
720}
721
722void
723pm_adb_poweroff()
724{
725	PMData p;
726
727	bzero(&p, sizeof p);
728	p.command = PMU_POWER_OFF;
729	p.num_data = 4;
730	p.s_buf = p.data;
731	p.r_buf = p.data;
732	strlcpy(p.data, "MATT", sizeof p.data);
733	pmgrop(&p);
734}
735
736void
737pm_read_date_time(u_long *time)
738{
739	PMData p;
740
741	p.command = PMU_READ_RTC;
742	p.num_data = 0;
743	p.s_buf = p.data;
744	p.r_buf = p.data;
745	pmgrop(&p);
746
747	bcopy(p.data, time, 4);
748}
749
750void
751pm_set_date_time(u_long time)
752{
753	PMData p;
754
755	p.command = PMU_SET_RTC;
756	p.num_data = 4;
757	p.s_buf = p.r_buf = p.data;
758	bcopy(&time, p.data, 4);
759	pmgrop(&p);
760}
761
762int
763pm_read_brightness()
764{
765	PMData p;
766
767	p.command = PMU_READ_BRIGHTNESS;
768	p.num_data = 1;		/* XXX why 1? */
769	p.s_buf = p.r_buf = p.data;
770	p.data[0] = 0;
771	pmgrop(&p);
772
773	return p.data[0];
774}
775
776void
777pm_set_brightness(int val)
778{
779	PMData p;
780
781	val = 0x7f - val / 2;
782	if (val < 0x08)
783		val = 0x08;
784	if (val > 0x78)
785		val = 0x78;
786
787	p.command = PMU_SET_BRIGHTNESS;
788	p.num_data = 1;
789	p.s_buf = p.r_buf = p.data;
790	p.data[0] = val;
791	pmgrop(&p);
792}
793
794void
795pm_init_brightness()
796{
797	int val;
798
799	val = pm_read_brightness();
800	pm_set_brightness(val);
801}
802
803void
804pm_eject_pcmcia(int slot)
805{
806	PMData p;
807
808	if (slot != 0 && slot != 1)
809		return;
810
811	p.command = PMU_EJECT_PCMCIA;
812	p.num_data = 1;
813	p.s_buf = p.r_buf = p.data;
814	p.data[0] = 5 + slot;	/* XXX */
815	pmgrop(&p);
816}
817
818
819/*
820 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
821 * for a clear description of the PMU results.
822 */
823
824int
825pm_battery_info(int battery, struct pmu_battery_info *info)
826{
827	PMData p;
828
829	p.command = PMU_SMART_BATTERY_STATE;
830	p.num_data = 1;
831	p.s_buf = p.r_buf = p.data;
832	p.data[0] = battery + 1;
833	pmgrop(&p);
834
835	info->flags = p.data[1];
836
837	switch (p.data[0]) {
838	case 3:
839	case 4:
840		info->cur_charge = p.data[2];
841		info->max_charge = p.data[3];
842		info->draw = *((signed char *)&p.data[4]);
843		info->voltage = p.data[5];
844		break;
845	case 5:
846		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
847		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
848		info->draw = *((signed short *)&p.data[6]);
849		info->voltage = ((p.data[8] << 8) | (p.data[7]));
850		break;
851	default:
852		/* XXX - Error condition */
853		info->cur_charge = 0;
854		info->max_charge = 0;
855		info->draw = 0;
856		info->voltage = 0;
857		break;
858	}
859
860	return 1;
861}
862
863
864
865int
866pm_read_nvram(int addr)
867{
868	PMData p;
869
870	p.command = PMU_READ_NVRAM;
871	p.num_data = 2;
872	p.s_buf = p.r_buf = p.data;
873	p.data[0] = addr >> 8;
874	p.data[1] = addr;
875	pmgrop(&p);
876
877	return p.data[0];
878}
879
880void
881pm_write_nvram(int addr, int val)
882{
883	PMData p;
884
885	p.command = PMU_WRITE_NVRAM;
886	p.num_data = 3;
887	p.s_buf = p.r_buf = p.data;
888	p.data[0] = addr >> 8;
889	p.data[1] = addr;
890	p.data[2] = val;
891	pmgrop(&p);
892}
893