xref: /netbsd-src/sys/arch/macppc/dev/pm_direct.c (revision cecde1b5250be188abd1ea1de5507e00d7ddefbe)
1 /*	$NetBSD: pm_direct.c,v 1.39 2024/06/02 13:28:46 andvar 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.39 2024/06/02 13:28:46 andvar 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 
66 extern 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  */
85 u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
86 u_int	pm_LCD_brightness = 0x0;
87 u_int	pm_LCD_contrast = 0x0;
88 u_int	pm_counter = 0;			/* clock count */
89 
90 static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
91 static int	pmu_nbatt;
92 static int	strinlist(const char *, char *, int);
93 static 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 */
96 signed 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 */
132 signed 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
174 void	pm_printerr(const char *, int, int, const char *);
175 #endif
176 
177 int	pm_wait_busy(int);
178 int	pm_wait_free(int);
179 
180 static int	pm_receive(u_char *);
181 static int	pm_send(u_char);
182 
183 /* these functions are called from adb_direct.c */
184 void	pm_setup_adb(void);
185 void	pm_check_adb_devices(int);
186 int	pm_adb_op(u_char *, adbComp *, volatile int *, int);
187 
188 /* these functions also use the variables of adb_direct.c */
189 void	pm_adb_get_TALK_result(PMData *);
190 void	pm_adb_get_ADB_data(PMData *);
191 
192 
193 /*
194  * These variables are in adb_direct.c.
195  */
196 extern u_char	*adbBuffer;	/* pointer to user data area */
197 extern adbComp	*adbCompRout;	/* pointer to the completion routine */
198 extern volatile int *adbCompData;	/* pointer to the completion routine data */
199 extern int	adbWaiting;	/* waiting for return data from the device */
200 extern int	adbWaitingCmd;	/* ADB command we are waiting for */
201 extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
202 
203 #define	ADB_MAX_MSG_LENGTH	16
204 #define	ADB_MAX_HDR_LENGTH	8
205 struct 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 };
215 extern	void	adb_pass_up(struct adbCommand *);
216 
217 #if 0
218 /*
219  * Define the external functions
220  */
221 extern int	zshard(int);		/* from zs.c */
222 #endif
223 
224 #ifdef ADB_DEBUG
225 /*
226  * This function dumps contents of the PMData
227  */
228 void
pm_printerr(const char * ttl,int rval,int num,const char * data)229 pm_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  */
245 void
pm_setup_adb(void)246 pm_setup_adb(void)
247 {
248 }
249 
250 /*
251  * Search for targ in list.  list is an area of listlen bytes
252  * containing null-terminated strings.
253  */
254 static int
strinlist(const char * targ,char * list,int listlen)255 strinlist(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  */
276 void
pm_init(void)277 pm_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  */
338 void
pm_check_adb_devices(int id)339 pm_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  */
351 int
pm_wait_busy(int delaycycles)352 pm_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  */
372 int
pm_wait_free(int delaycycles)373 pm_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  */
394 static int
pm_receive(u_char * data)395 pm_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  */
435 static int
pm_send(u_char data)436 pm_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  */
469 int
pmgrop(PMData * pmdata)470 pmgrop(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 interrupts 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  */
568 int
pm_intr(void * arg)569 pm_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 happened */
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  */
646 int
pm_adb_op(u_char * buffer,adbComp * compRout,volatile int * data,int command)647 pm_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 
768 void
pm_adb_get_TALK_result(PMData * pmdata)769 pm_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 
796 void
pm_adb_get_ADB_data(PMData * pmdata)797 pm_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 
823 void
pm_adb_restart(void)824 pm_adb_restart(void)
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 
835 void
pm_adb_poweroff(void)836 pm_adb_poweroff(void)
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 
848 void
pm_read_date_time(u_long * t)849 pm_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 
862 void
pm_set_date_time(u_long t)863 pm_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 
874 int
pm_read_brightness(void)875 pm_read_brightness(void)
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 
888 void
pm_set_brightness(int val)889 pm_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 
906 void
pm_init_brightness(void)907 pm_init_brightness(void)
908 {
909 	int val;
910 
911 	val = pm_read_brightness();
912 	pm_set_brightness(val);
913 }
914 
915 void
pm_eject_pcmcia(int slot)916 pm_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  */
934 static int
pm_battery_info_smart(int battery,struct pmu_battery_info * info)935 pm_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 
984 static int
pm_battery_info_legacy(int battery,struct pmu_battery_info * info,int ty)985 pm_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 
1049 int
pm_battery_info(int battery,struct pmu_battery_info * info)1050 pm_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 
1068 int
pm_read_nvram(int addr)1069 pm_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 
1083 void
pm_write_nvram(int addr,int val)1084 pm_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