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