pm_direct.c revision 1.25
1/*	$NetBSD: pm_direct.c,v 1.25 2005/02/01 03:24:29 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.25 2005/02/01 03:24:29 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/autoconf.h>
58#include <machine/cpu.h>
59
60#include <dev/ofw/openfirm.h>
61
62#include <macppc/dev/adbvar.h>
63#include <macppc/dev/pm_direct.h>
64#include <macppc/dev/viareg.h>
65
66extern int adb_polling;		/* Are we polling?  (Debugger mode) */
67
68/* hardware dependent values */
69#define ADBDelay 100		/* XXX */
70
71/* useful macros */
72#define PM_SR()			read_via_reg(VIA1, vSR)
73#define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
74#define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
75#define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
76
77#define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
78#define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
79#define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
80#define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
81
82/*
83 * Variables for internal use
84 */
85u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
86u_int	pm_LCD_brightness = 0x0;
87u_int	pm_LCD_contrast = 0x0;
88u_int	pm_counter = 0;			/* clock count */
89
90static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
91static int	pmu_nbatt;
92static int	strinlist(char *, char *, int);
93static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type;
94
95/* these values shows that number of data returned after 'send' cmd is sent */
96signed char pm_send_cmd_type[] = {
97	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
98	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
99	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
100	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
101	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
102	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
103	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
104	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
105	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
106	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
107	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
108	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
109	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
110	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
111	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
112	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
113	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
114	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
115	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
116	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
117	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
118	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
119	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
120	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
121	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
122	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
123	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
124	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
125	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
126	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
127	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
128	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
129};
130
131/* these values shows that number of data returned after 'receive' cmd is sent */
132signed char pm_receive_cmd_type[] = {
133	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
135	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
137	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
139	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
141	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
143	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
145	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
147	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
149	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
151	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
153	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
155	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
157	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
159	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
161	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
162	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
163	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
165};
166
167
168/*
169 * Define the private functions
170 */
171
172/* for debugging */
173#ifdef ADB_DEBUG
174void	pm_printerr __P((char *, int, int, char *));
175#endif
176
177int	pm_wait_busy __P((int));
178int	pm_wait_free __P((int));
179
180static int	pm_receive __P((u_char *));
181static int	pm_send __P((u_char));
182
183/* these functions are called from adb_direct.c */
184void	pm_setup_adb __P((void));
185void	pm_check_adb_devices __P((int));
186int	pm_adb_op __P((u_char *, void *, void *, int));
187
188/* these functions also use the variables of adb_direct.c */
189void	pm_adb_get_TALK_result __P((PMData *));
190void	pm_adb_get_ADB_data __P((PMData *));
191
192
193/*
194 * These variables are in adb_direct.c.
195 */
196extern u_char	*adbBuffer;	/* pointer to user data area */
197extern void	*adbCompRout;	/* pointer to the completion routine */
198extern void	*adbCompData;	/* pointer to the completion routine data */
199extern int	adbWaiting;	/* waiting for return data from the device */
200extern int	adbWaitingCmd;	/* ADB command we are waiting for */
201extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
202
203#define	ADB_MAX_MSG_LENGTH	16
204#define	ADB_MAX_HDR_LENGTH	8
205struct adbCommand {
206	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
207	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
208	u_char	*saveBuf;	/* where to save result */
209	u_char	*compRout;	/* completion routine pointer */
210	u_char	*compData;	/* completion routine data pointer */
211	u_int	cmd;		/* the original command for this data */
212	u_int	unsol;		/* 1 if packet was unsolicited */
213	u_int	ack_only;	/* 1 for no special processing */
214};
215extern	void	adb_pass_up __P((struct adbCommand *));
216
217#if 0
218/*
219 * Define the external functions
220 */
221extern int	zshard __P((int));		/* from zs.c */
222#endif
223
224#ifdef ADB_DEBUG
225/*
226 * This function dumps contents of the PMData
227 */
228void
229pm_printerr(ttl, rval, num, data)
230	char *ttl;
231	int rval;
232	int num;
233	char *data;
234{
235	int i;
236
237	printf("pm: %s:%04x %02x ", ttl, rval, num);
238	for (i = 0; i < num; i++)
239		printf("%02x ", data[i]);
240	printf("\n");
241}
242#endif
243
244
245
246/*
247 * Check the hardware type of the Power Manager
248 */
249void
250pm_setup_adb()
251{
252}
253
254static int
255strinlist(char *targ, char *list, int listlen)
256{
257	char	*str;
258	int	sl;
259
260	str = list;
261	while (listlen > 0) {
262		sl = strlen(str);
263		if (strncmp(targ, str, sl) == 0)
264			return 1;
265		str += sl+1;
266		listlen -= sl+1;
267	}
268	return 0;
269}
270
271/*
272 * Check the hardware type of the Power Manager
273 */
274void
275pm_init(void)
276{
277	uint32_t	regs[10];
278	PMData		pmdata;
279	char		compat[128];
280	int		clen, node, imask;
281
282	node = OF_peer(0);
283	if (node == -1) {
284		printf("pmu: Failed to get root");
285		return;
286	}
287	clen = OF_getprop(node, "compatible", compat, sizeof(compat));
288	if (clen <= 0) {
289		printf("pmu: failed to read root compatible data %d\n", clen);
290		return;
291	}
292
293	imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
294
295	if (strinlist("AAPL,3500", compat, clen) ||
296	    strinlist("AAPL,3400/2400", compat, clen)) {
297		/* How to distinguish BATT_COMET? */
298		pmu_nbatt = 1;
299		pmu_batt_type = BATT_HOOPER;
300		pmu_type = PMU_OHARE;
301	} else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
302		   strinlist("PowerBook1,1", compat, clen)) {
303		pmu_nbatt = 2;
304		pmu_batt_type = BATT_SMART;
305		pmu_type = PMU_G3;
306	} else {
307		pmu_nbatt = 1;
308		pmu_batt_type = BATT_SMART;
309		pmu_type = PMU_KEYLARGO;
310		node = getnodebyname(0, "power-mgt");
311		if (node == -1) {
312			printf("pmu: can't find power-mgt\n");
313			return;
314		}
315		clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
316		if (clen < 24) {
317			printf("pmu: failed to read prim-info\n");
318			return;
319		}
320		pmu_nbatt = regs[6] >> 16;
321	}
322
323	pmdata.command = PMU_SET_IMASK;
324	pmdata.num_data = 1;
325	pmdata.s_buf = pmdata.data;
326	pmdata.r_buf = pmdata.data;
327	pmdata.data[0] = imask;
328	pmgrop(&pmdata);
329}
330
331
332/*
333 * Check the existent ADB devices
334 */
335void
336pm_check_adb_devices(id)
337	int id;
338{
339	u_short ed = 0x1;
340
341	ed <<= id;
342	pm_existent_ADB_devices |= ed;
343}
344
345
346/*
347 * Wait until PM IC is busy
348 */
349int
350pm_wait_busy(delay)
351	int delay;
352{
353	while (PM_IS_ON) {
354#ifdef PM_GRAB_SI
355#if 0
356		zshard(0);		/* grab any serial interrupts */
357#else
358		(void)intr_dispatch(0x70);
359#endif
360#endif
361		if ((--delay) < 0)
362			return 1;	/* timeout */
363	}
364	return 0;
365}
366
367
368/*
369 * Wait until PM IC is free
370 */
371int
372pm_wait_free(delay)
373	int delay;
374{
375	while (PM_IS_OFF) {
376#ifdef PM_GRAB_SI
377#if 0
378		zshard(0);		/* grab any serial interrupts */
379#else
380		(void)intr_dispatch(0x70);
381#endif
382#endif
383		if ((--delay) < 0)
384			return 0;	/* timeout */
385	}
386	return 1;
387}
388
389
390
391/*
392 * Receive data from PMU
393 */
394static int
395pm_receive(data)
396	u_char *data;
397{
398	int i;
399	int rval;
400
401	rval = 0xffffcd34;
402
403	switch (1) {
404	default:
405		/* set VIA SR to input mode */
406		via_reg_or(VIA1, vACR, 0x0c);
407		via_reg_and(VIA1, vACR, ~0x10);
408		i = PM_SR();
409
410		PM_SET_STATE_ACKOFF();
411		if (pm_wait_busy((int)ADBDelay*32) != 0)
412			break;		/* timeout */
413
414		PM_SET_STATE_ACKON();
415		rval = 0xffffcd33;
416		if (pm_wait_free((int)ADBDelay*32) == 0)
417			break;		/* timeout */
418
419		*data = PM_SR();
420		rval = 0;
421
422		break;
423	}
424
425	PM_SET_STATE_ACKON();
426	via_reg_or(VIA1, vACR, 0x1c);
427
428	return rval;
429}
430
431
432
433/*
434 * Send data to PMU
435 */
436static int
437pm_send(data)
438	u_char data;
439{
440	int rval;
441
442	via_reg_or(VIA1, vACR, 0x1c);
443	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
444
445	PM_SET_STATE_ACKOFF();
446	rval = 0xffffcd36;
447	if (pm_wait_busy((int)ADBDelay*32) != 0) {
448		PM_SET_STATE_ACKON();
449
450		via_reg_or(VIA1, vACR, 0x1c);
451
452		return rval;
453	}
454
455	PM_SET_STATE_ACKON();
456	rval = 0xffffcd35;
457	if (pm_wait_free((int)ADBDelay*32) != 0)
458		rval = 0;
459
460	PM_SET_STATE_ACKON();
461	via_reg_or(VIA1, vACR, 0x1c);
462
463	return rval;
464}
465
466
467
468/*
469 * The PMgrOp routine
470 */
471int
472pmgrop(pmdata)
473	PMData *pmdata;
474{
475	int i;
476	int s;
477	u_char via1_vIER;
478	int rval = 0;
479	int num_pm_data = 0;
480	u_char pm_cmd;
481	short pm_num_rx_data;
482	u_char pm_data;
483	u_char *pm_buf;
484
485	s = splhigh();
486
487	/* disable all inetrrupts but PM */
488	via1_vIER = 0x10;
489	via1_vIER &= read_via_reg(VIA1, vIER);
490	write_via_reg(VIA1, vIER, via1_vIER);
491	if (via1_vIER != 0x0)
492		via1_vIER |= 0x80;
493
494	switch (pmdata->command) {
495	default:
496		/* wait until PM is free */
497		pm_cmd = (u_char)(pmdata->command & 0xff);
498		rval = 0xcd38;
499		if (pm_wait_free(ADBDelay * 4) == 0)
500			break;			/* timeout */
501
502		/* send PM command */
503		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
504			break;				/* timeout */
505
506		/* send number of PM data */
507		num_pm_data = pmdata->num_data;
508		if (pm_send_cmd_type[pm_cmd] < 0) {
509			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
510				break;		/* timeout */
511			pmdata->command = 0;
512		}
513		/* send PM data */
514		pm_buf = (u_char *)pmdata->s_buf;
515		for (i = 0 ; i < num_pm_data; i++)
516			if ((rval = pm_send(pm_buf[i])) != 0)
517				break;			/* timeout */
518		if (i != num_pm_data)
519			break;				/* timeout */
520
521
522		/* check if PM will send me data  */
523		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
524		pmdata->num_data = pm_num_rx_data;
525		if (pm_num_rx_data == 0) {
526			rval = 0;
527			break;				/* no return data */
528		}
529
530		/* receive PM command */
531		pm_data = pmdata->command;
532		pm_num_rx_data--;
533		if (pm_num_rx_data == 0)
534			if ((rval = pm_receive(&pm_data)) != 0) {
535				rval = 0xffffcd37;
536				break;
537			}
538		pmdata->command = pm_data;
539
540		/* receive number of PM data */
541		if (pm_num_rx_data < 0) {
542			if ((rval = pm_receive(&pm_data)) != 0)
543				break;		/* timeout */
544			num_pm_data = pm_data;
545		} else
546			num_pm_data = pm_num_rx_data;
547		pmdata->num_data = num_pm_data;
548
549		/* receive PM data */
550		pm_buf = (u_char *)pmdata->r_buf;
551		for (i = 0; i < num_pm_data; i++) {
552			if ((rval = pm_receive(&pm_data)) != 0)
553				break;			/* timeout */
554			pm_buf[i] = pm_data;
555		}
556
557		rval = 0;
558	}
559
560	/* restore former value */
561	write_via_reg(VIA1, vIER, via1_vIER);
562	splx(s);
563
564	return rval;
565}
566
567
568/*
569 * My PMU interrupt routine
570 */
571int
572pm_intr(void *arg)
573{
574	int s;
575	int rval;
576	PMData pmdata;
577
578	s = splhigh();
579
580	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
581						/* ask PM what happend */
582	pmdata.command = PMU_INT_ACK;
583	pmdata.num_data = 0;
584	pmdata.s_buf = &pmdata.data[2];
585	pmdata.r_buf = &pmdata.data[2];
586	rval = pmgrop(&pmdata);
587	if (rval != 0) {
588#ifdef ADB_DEBUG
589		if (adb_debug)
590			printf("pm: PM is not ready. error code: %08x\n", rval);
591#endif
592		splx(s);
593		return 0;
594	}
595
596	switch ((u_int)(pmdata.data[2] & 0xff)) {
597	case 0x00:		/* no event pending? */
598		break;
599	case 0x80:		/* 1 sec interrupt? */
600		pm_counter++;
601		break;
602	case 0x08:		/* Brightness/Contrast button on LCD panel */
603		/* get brightness and contrast of the LCD */
604		pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
605		pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
606
607		/* this is experimental code */
608		pmdata.command = PMU_SET_BRIGHTNESS;
609		pmdata.num_data = 1;
610		pmdata.s_buf = pmdata.data;
611		pmdata.r_buf = pmdata.data;
612		pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
613		if (pm_LCD_brightness < 0x08)
614			pm_LCD_brightness = 0x08;
615		if (pm_LCD_brightness > 0x78)
616			pm_LCD_brightness = 0x78;
617		pmdata.data[0] = pm_LCD_brightness;
618		rval = pmgrop(&pmdata);
619		break;
620
621	case 0x10:		/* ADB data requested by TALK command */
622	case 0x14:
623		pm_adb_get_TALK_result(&pmdata);
624		break;
625	case 0x16:		/* ADB device event */
626	case 0x18:
627	case 0x1e:
628		pm_adb_get_ADB_data(&pmdata);
629		break;
630	default:
631#ifdef ADB_DEBUG
632		if (adb_debug)
633			pm_printerr("driver does not support this event.",
634			    pmdata.data[2], pmdata.num_data,
635			    pmdata.data);
636#endif
637		break;
638	}
639
640	splx(s);
641
642	return 1;
643}
644
645
646/*
647 * Synchronous ADBOp routine for the Power Manager
648 */
649int
650pm_adb_op(buffer, compRout, data, command)
651	u_char *buffer;
652	void *compRout;
653	void *data;
654	int command;
655{
656	int i;
657	int s;
658	int rval;
659	int timo;
660	PMData pmdata;
661	struct adbCommand packet;
662
663	if (adbWaiting == 1)
664		return 1;
665
666	s = splhigh();
667	write_via_reg(VIA1, vIER, 0x10);
668
669 	adbBuffer = buffer;
670	adbCompRout = compRout;
671	adbCompData = data;
672
673	pmdata.command = PMU_ADB_CMD;
674	pmdata.s_buf = pmdata.data;
675	pmdata.r_buf = pmdata.data;
676
677	/* if the command is LISTEN, add number of ADB data to number of PM data */
678	if ((command & 0xc) == 0x8) {
679		if (buffer != (u_char *)0)
680			pmdata.num_data = buffer[0] + 3;
681	} else {
682		pmdata.num_data = 3;
683	}
684
685	pmdata.data[0] = (u_char)(command & 0xff);
686	pmdata.data[1] = 0;
687	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
688		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
689			pmdata.data[2] = buffer[0];		/* number of data */
690			for (i = 0; i < buffer[0]; i++)
691				pmdata.data[3 + i] = buffer[1 + i];
692		} else
693			pmdata.data[2] = 0;
694	} else
695		pmdata.data[2] = 0;
696
697	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
698		/* set up stuff for adb_pass_up */
699		packet.data[0] = 1 + pmdata.data[2];
700		packet.data[1] = command;
701		for (i = 0; i < pmdata.data[2]; i++)
702			packet.data[i+2] = pmdata.data[i+3];
703		packet.saveBuf = adbBuffer;
704		packet.compRout = adbCompRout;
705		packet.compData = adbCompData;
706		packet.cmd = command;
707		packet.unsol = 0;
708		packet.ack_only = 1;
709		adb_polling = 1;
710		adb_pass_up(&packet);
711		adb_polling = 0;
712	}
713
714	rval = pmgrop(&pmdata);
715	if (rval != 0) {
716		splx(s);
717		return 1;
718	}
719
720	delay(10000);
721
722	adbWaiting = 1;
723	adbWaitingCmd = command;
724
725	PM_VIA_INTR_ENABLE();
726
727	/* wait until the PM interrupt has occurred */
728	timo = 0x80000;
729	while (adbWaiting == 1) {
730		if (read_via_reg(VIA1, vIFR) & 0x14)
731			pm_intr(NULL);
732#ifdef PM_GRAB_SI
733#if 0
734			zshard(0);		/* grab any serial interrupts */
735#else
736			(void)intr_dispatch(0x70);
737#endif
738#endif
739		if ((--timo) < 0) {
740			/* Try to take an interrupt anyway, just in case.
741			 * This has been observed to happen on my ibook
742			 * when i press a key after boot and before adb
743			 * is attached;  For example, when booting with -d.
744			 */
745			pm_intr(NULL);
746			if (adbWaiting) {
747				printf("pm_adb_op: timeout. command = 0x%x\n",command);
748				splx(s);
749				return 1;
750			}
751#ifdef ADB_DEBUG
752			else {
753				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
754			}
755#endif
756		}
757	}
758
759	/* this command enables the interrupt by operating ADB devices */
760	pmdata.command = PMU_ADB_CMD;
761	pmdata.num_data = 4;
762	pmdata.s_buf = pmdata.data;
763	pmdata.r_buf = pmdata.data;
764	pmdata.data[0] = 0x00;
765	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
766	pmdata.data[2] = 0x00;
767	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
768	rval = pmgrop(&pmdata);
769
770	splx(s);
771	return rval;
772}
773
774
775void
776pm_adb_get_TALK_result(pmdata)
777	PMData *pmdata;
778{
779	int i;
780	struct adbCommand packet;
781
782	/* set up data for adb_pass_up */
783	packet.data[0] = pmdata->num_data-1;
784	packet.data[1] = pmdata->data[3];
785	for (i = 0; i <packet.data[0]-1; i++)
786		packet.data[i+2] = pmdata->data[i+4];
787
788	packet.saveBuf = adbBuffer;
789	packet.compRout = adbCompRout;
790	packet.compData = adbCompData;
791	packet.unsol = 0;
792	packet.ack_only = 0;
793	adb_polling = 1;
794	adb_pass_up(&packet);
795	adb_polling = 0;
796
797	adbWaiting = 0;
798	adbBuffer = (long)0;
799	adbCompRout = (long)0;
800	adbCompData = (long)0;
801}
802
803
804void
805pm_adb_get_ADB_data(pmdata)
806	PMData *pmdata;
807{
808	int i;
809	struct adbCommand packet;
810
811	if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
812	    pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
813	    ((pmdata->data[2] & ~1) == 0xf4)) {
814		if (pmdata->data[2] == 0xf4) {
815			pm_eject_pcmcia(0);
816		} else {
817			pm_eject_pcmcia(1);
818		}
819		return;
820	}
821	/* set up data for adb_pass_up */
822	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
823	packet.data[1] = pmdata->data[3];	/* ADB command */
824	for (i = 0; i <packet.data[0]-1; i++)
825		packet.data[i+2] = pmdata->data[i+4];
826	packet.unsol = 1;
827	packet.ack_only = 0;
828	adb_pass_up(&packet);
829}
830
831
832void
833pm_adb_restart()
834{
835	PMData p;
836
837	p.command = PMU_RESET_CPU;
838	p.num_data = 0;
839	p.s_buf = p.data;
840	p.r_buf = p.data;
841	pmgrop(&p);
842}
843
844void
845pm_adb_poweroff()
846{
847	PMData p;
848
849	p.command = PMU_POWER_OFF;
850	p.num_data = 4;
851	p.s_buf = p.data;
852	p.r_buf = p.data;
853	strcpy(p.data, "MATT");
854	pmgrop(&p);
855}
856
857void
858pm_read_date_time(time)
859	u_long *time;
860{
861	PMData p;
862
863	p.command = PMU_READ_RTC;
864	p.num_data = 0;
865	p.s_buf = p.data;
866	p.r_buf = p.data;
867	pmgrop(&p);
868
869	memcpy(time, p.data, 4);
870}
871
872void
873pm_set_date_time(time)
874	u_long time;
875{
876	PMData p;
877
878	p.command = PMU_SET_RTC;
879	p.num_data = 4;
880	p.s_buf = p.r_buf = p.data;
881	memcpy(p.data, &time, 4);
882	pmgrop(&p);
883}
884
885int
886pm_read_brightness()
887{
888	PMData p;
889
890	p.command = PMU_READ_BRIGHTNESS;
891	p.num_data = 1;		/* XXX why 1? */
892	p.s_buf = p.r_buf = p.data;
893	p.data[0] = 0;
894	pmgrop(&p);
895
896	return p.data[0];
897}
898
899void
900pm_set_brightness(val)
901	int val;
902{
903	PMData p;
904
905	val = 0x7f - val / 2;
906	if (val < 0x08)
907		val = 0x08;
908	if (val > 0x78)
909		val = 0x78;
910
911	p.command = PMU_SET_BRIGHTNESS;
912	p.num_data = 1;
913	p.s_buf = p.r_buf = p.data;
914	p.data[0] = val;
915	pmgrop(&p);
916}
917
918void
919pm_init_brightness()
920{
921	int val;
922
923	val = pm_read_brightness();
924	pm_set_brightness(val);
925}
926
927void
928pm_eject_pcmcia(slot)
929	int slot;
930{
931	PMData p;
932
933	if (slot != 0 && slot != 1)
934		return;
935
936	p.command = PMU_EJECT_PCMCIA;
937	p.num_data = 1;
938	p.s_buf = p.r_buf = p.data;
939	p.data[0] = 5 + slot;	/* XXX */
940	pmgrop(&p);
941}
942
943/*
944 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
945 * for a clear description of the PMU results.
946 */
947static int
948pm_battery_info_smart(int battery, struct pmu_battery_info *info)
949{
950	PMData p;
951
952	p.command = PMU_SMART_BATTERY_STATE;
953	p.num_data = 1;
954	p.s_buf = p.r_buf = p.data;
955	p.data[0] = battery + 1;
956	pmgrop(&p);
957
958	info->flags = p.data[1];
959
960	info->secs_remaining = 0;
961	switch (p.data[0]) {
962	case 3:
963	case 4:
964		info->cur_charge = p.data[2];
965		info->max_charge = p.data[3];
966		info->draw = *((signed char *)&p.data[4]);
967		info->voltage = p.data[5];
968		break;
969	case 5:
970		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
971		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
972		info->draw = *((signed short *)&p.data[6]);
973		info->voltage = ((p.data[8] << 8) | (p.data[7]));
974		break;
975	default:
976		/* XXX - Error condition */
977		info->cur_charge = 0;
978		info->max_charge = 0;
979		info->draw = 0;
980		info->voltage = 0;
981		break;
982	}
983	if (info->draw) {
984		if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
985			info->secs_remaining =
986				((info->max_charge - info->cur_charge) * 3600)
987				/ info->draw;
988		} else {
989			info->secs_remaining =
990				(info->cur_charge * 3600) / -info->draw;
991		}
992	}
993
994	return 1;
995}
996
997static int
998pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
999{
1000	PMData p;
1001	long pcharge=0, charge, vb, vmax, lmax;
1002	long vmax_charging, vmax_charged, amperage, voltage;
1003
1004	p.command = PMU_BATTERY_STATE;
1005	p.num_data = 0;
1006	p.s_buf = p.r_buf = p.data;
1007	pmgrop(&p);
1008
1009	info->flags = p.data[0];
1010
1011	if (info->flags & PMU_PWR_BATT_PRESENT) {
1012		if (ty == BATT_COMET) {
1013			vmax_charging = 213;
1014			vmax_charged = 189;
1015			lmax = 6500;
1016		} else {
1017			/* Experimental values */
1018			vmax_charging = 365;
1019			vmax_charged = 365;
1020			lmax = 6500;
1021		}
1022		vmax = vmax_charged;
1023		vb = (p.data[1] << 8) | p.data[2];
1024		voltage = (vb * 256 + 72665) / 10;
1025		amperage = (unsigned char) p.data[5];
1026		if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1027			if (amperage > 200)
1028				vb += ((amperage - 200) * 15)/100;
1029		} else if (info->flags & PMU_PWR_BATT_CHARGING) {
1030			vb = (vb * 97) / 100;
1031			vmax = vmax_charging;
1032		}
1033		charge = (100 * vb) / vmax;
1034		if (info->flags & PMU_PWR_PCHARGE_RESET) {
1035			pcharge = (p.data[6] << 8) | p.data[7];
1036			if (pcharge > lmax)
1037				pcharge = lmax;
1038			pcharge *= 100;
1039			pcharge = 100 - pcharge / lmax;
1040			if (pcharge < charge)
1041				charge = pcharge;
1042		}
1043		info->cur_charge = charge;
1044		info->max_charge = 100;
1045		info->draw = -amperage;
1046		info->voltage = voltage;
1047		if (amperage > 0)
1048			info->secs_remaining = (charge * 16440) / amperage;
1049		else
1050			info->secs_remaining = 0;
1051	} else {
1052		info->cur_charge = 0;
1053		info->max_charge = 0;
1054		info->draw = 0;
1055		info->voltage = 0;
1056		info->secs_remaining = 0;
1057	}
1058
1059	return 1;
1060}
1061
1062int
1063pm_battery_info(int battery, struct pmu_battery_info *info)
1064{
1065
1066	if (battery > pmu_nbatt)
1067		return 0;
1068
1069	switch (pmu_batt_type) {
1070	case BATT_COMET:
1071	case BATT_HOOPER:
1072		return pm_battery_info_legacy(battery, info, pmu_batt_type);
1073
1074	case BATT_SMART:
1075		return pm_battery_info_smart(battery, info);
1076	}
1077
1078	return 0;
1079}
1080
1081int
1082pm_read_nvram(addr)
1083	int addr;
1084{
1085	PMData p;
1086
1087	p.command = PMU_READ_NVRAM;
1088	p.num_data = 2;
1089	p.s_buf = p.r_buf = p.data;
1090	p.data[0] = addr >> 8;
1091	p.data[1] = addr;
1092	pmgrop(&p);
1093
1094	return p.data[0];
1095}
1096
1097void
1098pm_write_nvram(addr, val)
1099	int addr, val;
1100{
1101	PMData p;
1102
1103	p.command = PMU_WRITE_NVRAM;
1104	p.num_data = 3;
1105	p.s_buf = p.r_buf = p.data;
1106	p.data[0] = addr >> 8;
1107	p.data[1] = addr;
1108	p.data[2] = val;
1109	pmgrop(&p);
1110}
1111