pm_direct.c revision 1.12
1/*	$OpenBSD: pm_direct.c,v 1.12 2003/10/16 03:31:25 drahn 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
333/*
334 * Send data to PM for the PB Duo series and the PB 5XX series
335 */
336int
337pm_send_pm2(data)
338	u_char data;
339{
340	int rval;
341
342	via_reg_or(VIA1, vACR, 0x1c);
343	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
344
345	PM_SET_STATE_ACKOFF();
346	rval = 0xffffcd36;
347	if (pm_wait_busy((int)ADBDelay*32) != 0) {
348		PM_SET_STATE_ACKON();
349
350		via_reg_or(VIA1, vACR, 0x1c);
351
352		return rval;
353	}
354
355	PM_SET_STATE_ACKON();
356	rval = 0xffffcd35;
357	if (pm_wait_free((int)ADBDelay*32) != 0)
358		rval = 0;
359
360	PM_SET_STATE_ACKON();
361	via_reg_or(VIA1, vACR, 0x1c);
362
363	return rval;
364}
365
366
367
368/*
369 * My PMgrOp routine for the PB Duo series and the PB 5XX series
370 */
371int
372pm_pmgrop_pm2(PMData *pmdata)
373{
374	int i;
375	int s;
376	u_char via1_vIER;
377	int rval = 0;
378	int num_pm_data = 0;
379	u_char pm_cmd;
380	short pm_num_rx_data;
381	u_char pm_data;
382	u_char *pm_buf;
383
384	s = splhigh();
385
386	/* disable all inetrrupts but PM */
387	via1_vIER = 0x10;
388	via1_vIER &= read_via_reg(VIA1, vIER);
389	write_via_reg(VIA1, vIER, via1_vIER);
390	if (via1_vIER != 0x0)
391		via1_vIER |= 0x80;
392
393	switch (pmdata->command) {
394	default:
395		/* wait until PM is free */
396		pm_cmd = (u_char)(pmdata->command & 0xff);
397		rval = 0xcd38;
398		if (pm_wait_free(ADBDelay * 4) == 0)
399			break;			/* timeout */
400
401		/* send PM command */
402		if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff))))
403			break;				/* timeout */
404
405		/* send number of PM data */
406		num_pm_data = pmdata->num_data;
407		if (pm_send_cmd_type[pm_cmd] < 0) {
408			if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
409				break;		/* timeout */
410			pmdata->command = 0;
411		}
412		/* send PM data */
413		pm_buf = (u_char *)pmdata->s_buf;
414		for (i = 0 ; i < num_pm_data; i++)
415			if ((rval = pm_send_pm2(pm_buf[i])) != 0)
416				break;			/* timeout */
417		if (i != num_pm_data)
418			break;				/* timeout */
419
420
421		/* check if PM will send me data  */
422		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
423		pmdata->num_data = pm_num_rx_data;
424		if (pm_num_rx_data == 0) {
425			rval = 0;
426			break;				/* no return data */
427		}
428
429		/* receive PM command */
430		pm_data = pmdata->command;
431		pm_num_rx_data--;
432		if (pm_num_rx_data == 0)
433			if ((rval = pm_receive_pm2(&pm_data)) != 0) {
434				rval = 0xffffcd37;
435				break;
436			}
437		pmdata->command = pm_data;
438
439		/* receive number of PM data */
440		if (pm_num_rx_data < 0) {
441			if ((rval = pm_receive_pm2(&pm_data)) != 0)
442				break;		/* timeout */
443			num_pm_data = pm_data;
444		} else
445			num_pm_data = pm_num_rx_data;
446		pmdata->num_data = num_pm_data;
447
448		/* receive PM data */
449		pm_buf = (u_char *)pmdata->r_buf;
450		for (i = 0; i < num_pm_data; i++) {
451			if ((rval = pm_receive_pm2(&pm_data)) != 0)
452				break;			/* timeout */
453			pm_buf[i] = pm_data;
454		}
455
456		rval = 0;
457	}
458
459	/* restore former value */
460	write_via_reg(VIA1, vIER, via1_vIER);
461	splx(s);
462
463	return rval;
464}
465
466
467/*
468 * My PM interrupt routine for the PB Duo series and the PB 5XX series
469 */
470void
471pm_intr_pm2()
472{
473	int s;
474	int rval;
475	PMData pmdata;
476
477	s = splhigh();
478
479	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
480						/* ask PM what happend */
481	pmdata.command = 0x78;
482	pmdata.num_data = 0;
483	pmdata.s_buf = &pmdata.data[2];
484	pmdata.r_buf = &pmdata.data[2];
485	rval = pm_pmgrop_pm2(&pmdata);
486	if (rval != 0) {
487#ifdef ADB_DEBUG
488		if (adb_debug)
489			printf("pm: PM is not ready. error code: %08x\n", rval);
490#endif
491		splx(s);
492		return;
493	}
494
495	switch ((u_int)(pmdata.data[2] & 0xff)) {
496	case 0x00:		/* 1 sec interrupt? */
497		break;
498	case PMU_INT_TICK:	/* 1 sec interrupt? */
499		break;
500	case PMU_INT_SNDBRT:	/* Brightness/Contrast button on LCD panel */
501		break;
502	case PMU_INT_ADB:	/* ADB data requested by TALK command */
503	case PMU_INT_ADB|PMU_INT_ADB_AUTO:
504		pm_adb_get_TALK_result(&pmdata);
505		break;
506	case 0x16:		/* ADB device event */
507	case 0x18:
508	case 0x1e:
509	case PMU_INT_WAKEUP:
510		pm_adb_get_ADB_data(&pmdata);
511		break;
512	default:
513#ifdef ADB_DEBUG
514		if (adb_debug)
515			pm_printerr("driver does not support this event.",
516			    pmdata.data[2], pmdata.num_data,
517			    pmdata.data);
518#endif
519		break;
520	}
521
522	splx(s);
523}
524
525
526/*
527 * My PMgrOp routine
528 */
529int
530pmgrop(PMData *pmdata)
531{
532	switch (pmHardware) {
533	case PM_HW_PB5XX:
534		return (pm_pmgrop_pm2(pmdata));
535	default:
536		/* return (pmgrop_mrg(pmdata)); */
537		return 1;
538	}
539}
540
541
542/*
543 * My PM interrupt routine
544 */
545void
546pm_intr()
547{
548	switch (pmHardware) {
549	case PM_HW_PB5XX:
550		pm_intr_pm2();
551		break;
552	default:
553		break;
554	}
555}
556
557
558
559/*
560 * Synchronous ADBOp routine for the Power Manager
561 */
562int
563pm_adb_op(u_char *buffer, void *compRout, void *data, int command)
564{
565	int i;
566	int s;
567	int rval;
568	int ndelay;
569	PMData pmdata;
570	struct adbCommand packet;
571
572	if (adbWaiting == 1)
573		return 1;
574
575	s = splhigh();
576	write_via_reg(VIA1, vIER, 0x10);
577
578 	adbBuffer = buffer;
579	adbCompRout = compRout;
580	adbCompData = data;
581
582	pmdata.command = 0x20;
583	pmdata.s_buf = pmdata.data;
584	pmdata.r_buf = pmdata.data;
585
586	/*
587	 * if the command is LISTEN,
588	 * add number of ADB data to number of PM data
589	 */
590	if ((command & 0xc) == 0x8) {
591		if (buffer != (u_char *)0)
592			pmdata.num_data = buffer[0] + 3;
593	} else
594		pmdata.num_data = 3;
595
596	pmdata.data[0] = (u_char)(command & 0xff);
597	pmdata.data[1] = 0;
598	/* if the command is LISTEN, copy ADB data to PM buffer */
599	if ((command & 0xc) == 0x8) {
600		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
601			pmdata.data[2] = buffer[0];	/* number of data */
602			for (i = 0; i < buffer[0]; i++)
603				pmdata.data[3 + i] = buffer[1 + i];
604		} else
605			pmdata.data[2] = 0;
606	} else
607		pmdata.data[2] = 0;
608
609	if ((command & 0xc) != 0xc) {	/* if the command is not TALK */
610		/* set up stuff for adb_pass_up */
611		packet.data[0] = 1 + pmdata.data[2];
612		packet.data[1] = command;
613		for (i = 0; i < pmdata.data[2]; i++)
614			packet.data[i+2] = pmdata.data[i+3];
615		packet.saveBuf = adbBuffer;
616		packet.compRout = adbCompRout;
617		packet.compData = adbCompData;
618		packet.cmd = command;
619		packet.unsol = 0;
620		packet.ack_only = 1;
621		adb_polling = 1;
622		adb_pass_up(&packet);
623		adb_polling = 0;
624	}
625
626	rval = pmgrop(&pmdata);
627	if (rval != 0) {
628		splx(s);
629		return 1;
630	}
631
632	delay (1000);
633
634	adbWaiting = 1;
635	adbWaitingCmd = command;
636
637	PM_VIA_INTR_ENABLE();
638
639	/* wait until the PM interrupt is occurred */
640	ndelay = 0x8000;
641	while (adbWaiting == 1) {
642		if (read_via_reg(VIA1, vIFR) != 0)
643			pm_intr();
644#ifdef PM_GRAB_SI
645			(void)intr_dispatch(0x70);
646#endif
647		if ((--ndelay) < 0) {
648			splx(s);
649			return 1;
650		}
651		delay(10);
652	}
653
654	/* this command enables the interrupt by operating ADB devices */
655	pmdata.command = 0x20;
656	pmdata.num_data = 4;
657	pmdata.s_buf = pmdata.data;
658	pmdata.r_buf = pmdata.data;
659	pmdata.data[0] = 0x00;
660	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
661	pmdata.data[2] = 0x00;
662	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
663	rval = pmgrop(&pmdata);
664
665	splx(s);
666	return rval;
667}
668
669
670void
671pm_adb_get_TALK_result(PMData *pmdata)
672{
673	int i;
674	struct adbCommand packet;
675
676	/* set up data for adb_pass_up */
677	packet.data[0] = pmdata->num_data-1;
678	packet.data[1] = pmdata->data[3];
679	for (i = 0; i <packet.data[0]-1; i++)
680		packet.data[i+2] = pmdata->data[i+4];
681
682	packet.saveBuf = adbBuffer;
683	packet.compRout = adbCompRout;
684	packet.compData = adbCompData;
685	packet.unsol = 0;
686	packet.ack_only = 0;
687	adb_polling = 1;
688	adb_pass_up(&packet);
689	adb_polling = 0;
690
691	adbWaiting = 0;
692	adbBuffer = (long)0;
693	adbCompRout = (long)0;
694	adbCompData = (long)0;
695}
696
697
698void
699pm_adb_get_ADB_data(PMData *pmdata)
700{
701	int i;
702	struct adbCommand packet;
703
704	/* set up data for adb_pass_up */
705	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
706	packet.data[1] = pmdata->data[3];	/* ADB command */
707	for (i = 0; i <packet.data[0]-1; i++)
708		packet.data[i+2] = pmdata->data[i+4];
709	packet.unsol = 1;
710	packet.ack_only = 0;
711	adb_pass_up(&packet);
712}
713
714void
715pm_adb_restart()
716{
717	PMData p;
718
719	p.command = PMU_RESET_CPU;
720	p.num_data = 0;
721	p.s_buf = p.data;
722	p.r_buf = p.data;
723	pmgrop(&p);
724}
725
726void
727pm_adb_poweroff()
728{
729	PMData p;
730
731	bzero(&p, sizeof p);
732	p.command = PMU_POWER_OFF;
733	p.num_data = 4;
734	p.s_buf = p.data;
735	p.r_buf = p.data;
736	strlcpy(p.data, "MATT", sizeof p.data);
737	pmgrop(&p);
738}
739
740void
741pm_read_date_time(u_long *time)
742{
743	PMData p;
744
745	p.command = PMU_READ_RTC;
746	p.num_data = 0;
747	p.s_buf = p.data;
748	p.r_buf = p.data;
749	pmgrop(&p);
750
751	bcopy(p.data, time, 4);
752}
753
754void
755pm_set_date_time(u_long time)
756{
757	PMData p;
758
759	p.command = PMU_SET_RTC;
760	p.num_data = 4;
761	p.s_buf = p.r_buf = p.data;
762	bcopy(&time, p.data, 4);
763	pmgrop(&p);
764}
765
766int
767pm_read_brightness()
768{
769	PMData p;
770
771	p.command = PMU_READ_BRIGHTNESS;
772	p.num_data = 1;		/* XXX why 1? */
773	p.s_buf = p.r_buf = p.data;
774	p.data[0] = 0;
775	pmgrop(&p);
776
777	return p.data[0];
778}
779
780void
781pm_set_brightness(int val)
782{
783	PMData p;
784
785	val = 0x7f - val / 2;
786	if (val < 0x08)
787		val = 0x08;
788	if (val > 0x78)
789		val = 0x78;
790
791	p.command = PMU_SET_BRIGHTNESS;
792	p.num_data = 1;
793	p.s_buf = p.r_buf = p.data;
794	p.data[0] = val;
795	pmgrop(&p);
796}
797
798void
799pm_init_brightness()
800{
801	int val;
802
803	val = pm_read_brightness();
804	pm_set_brightness(val);
805}
806
807void
808pm_eject_pcmcia(int slot)
809{
810	PMData p;
811
812	if (slot != 0 && slot != 1)
813		return;
814
815	p.command = PMU_EJECT_PCMCIA;
816	p.num_data = 1;
817	p.s_buf = p.r_buf = p.data;
818	p.data[0] = 5 + slot;	/* XXX */
819	pmgrop(&p);
820}
821
822
823/*
824 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
825 * for a clear description of the PMU results.
826 */
827
828int
829pm_battery_info(int battery, struct pmu_battery_info *info)
830{
831	PMData p;
832
833	p.command = PMU_SMART_BATTERY_STATE;
834	p.num_data = 1;
835	p.s_buf = p.r_buf = p.data;
836	p.data[0] = battery + 1;
837	pmgrop(&p);
838
839	info->flags = p.data[1];
840
841	switch (p.data[0]) {
842	case 3:
843	case 4:
844		info->cur_charge = p.data[2];
845		info->max_charge = p.data[3];
846		info->draw = *((signed char *)&p.data[4]);
847		info->voltage = p.data[5];
848		break;
849	case 5:
850		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
851		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
852		info->draw = *((signed short *)&p.data[6]);
853		info->voltage = ((p.data[8] << 8) | (p.data[7]));
854		break;
855	default:
856		/* XXX - Error condition */
857		info->cur_charge = 0;
858		info->max_charge = 0;
859		info->draw = 0;
860		info->voltage = 0;
861		break;
862	}
863
864	return 1;
865}
866
867
868
869int
870pm_read_nvram(int addr)
871{
872	PMData p;
873
874	p.command = PMU_READ_NVRAM;
875	p.num_data = 2;
876	p.s_buf = p.r_buf = p.data;
877	p.data[0] = addr >> 8;
878	p.data[1] = addr;
879	pmgrop(&p);
880
881	return p.data[0];
882}
883
884void
885pm_write_nvram(int addr, int val)
886{
887	PMData p;
888
889	p.command = PMU_WRITE_NVRAM;
890	p.num_data = 3;
891	p.s_buf = p.r_buf = p.data;
892	p.data[0] = addr >> 8;
893	p.data[1] = addr;
894	p.data[2] = val;
895	pmgrop(&p);
896}
897