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