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