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