pm_direct.c revision 1.24 1 /* $NetBSD: pm_direct.c,v 1.24 2005/02/01 03:08:16 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.24 2005/02/01 03:08:16 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
66 extern int adb_polling; /* Are we polling? (Debugger mode) */
67
68 /* hardware dependent values */
69 #define ADBDelay 100 /* XXX */
70 #define HwCfgFlags3 0x20000 /* XXX */
71
72 /* define the types of the Power Manager */
73 #define PM_HW_UNKNOWN 0x00 /* don't know */
74 #define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */
75 #define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */
76
77 /* useful macros */
78 #define PM_SR() read_via_reg(VIA1, vSR)
79 #define PM_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x90)
80 #define PM_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x10)
81 #define PM_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x90)
82 #if 0
83 #define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x04)
84 #define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x04)
85 #define PM_IS_ON (0x02 == (read_via_reg(VIA2, vBufB) & 0x02))
86 #define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x02))
87 #else
88 #define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x10)
89 #define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x10)
90 #define PM_IS_ON (0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
91 #define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
92 #endif
93
94 /*
95 * Variables for internal use
96 */
97 int pmHardware = PM_HW_UNKNOWN;
98 u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */
99 u_int pm_LCD_brightness = 0x0;
100 u_int pm_LCD_contrast = 0x0;
101 u_int pm_counter = 0; /* clock count */
102
103 static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
104 static int pmu_nbatt;
105 static int strinlist(char *, char *, int);
106 static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type;
107
108 /* these values shows that number of data returned after 'send' cmd is sent */
109 signed char pm_send_cmd_type[] = {
110 -1, -1, -1, -1, -1, -1, -1, -1,
111 -1, -1, -1, -1, -1, -1, -1, -1,
112 0x01, 0x01, -1, -1, -1, -1, -1, -1,
113 0x00, 0x00, -1, -1, -1, -1, -1, 0x00,
114 -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1,
115 0x00, -1, -1, -1, -1, -1, -1, -1,
116 0x04, 0x14, -1, 0x03, -1, -1, -1, -1,
117 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1,
118 0x01, 0x01, -1, -1, -1, -1, -1, -1,
119 0x00, 0x00, -1, -1, 0x01, -1, -1, -1,
120 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01,
121 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1,
122 0x02, -1, -1, -1, -1, -1, -1, -1,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1,
124 0x01, 0x01, 0x01, -1, -1, -1, -1, -1,
125 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04,
126 0x04, -1, 0x00, -1, -1, -1, -1, -1,
127 0x00, -1, -1, -1, -1, -1, -1, -1,
128 0x01, 0x02, -1, -1, -1, -1, -1, -1,
129 0x00, 0x00, -1, -1, -1, -1, -1, -1,
130 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1,
131 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1,
132 -1, -1, -1, -1, -1, -1, -1, -1,
133 -1, -1, -1, -1, -1, -1, -1, -1,
134 -1, -1, -1, -1, -1, -1, -1, -1,
135 -1, -1, -1, -1, -1, -1, -1, -1,
136 0x00, -1, -1, -1, -1, -1, -1, -1,
137 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1,
138 -1, 0x04, 0x00, -1, -1, -1, -1, -1,
139 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00,
140 -1, -1, -1, -1, -1, -1, -1, -1,
141 -1, -1, -1, -1, -1, -1, -1, -1
142 };
143
144 /* these values shows that number of data returned after 'receive' cmd is sent */
145 signed char pm_receive_cmd_type[] = {
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 -1, -1, -1, -1, -1, -1, -1, -1,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x02, 0x02, -1, -1, -1, -1, -1, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 -1, -1, -1, -1, -1, -1, -1, -1,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0x05, 0x15, -1, 0x02, -1, -1, -1, -1,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x02, 0x02, -1, -1, -1, -1, -1, -1,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 -1, -1, -1, -1, -1, -1, 0x01, 0x01,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x06, -1, -1, -1, -1, -1, -1, -1,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x02, 0x02, -1, -1, -1, -1, -1, -1,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 -1, -1, -1, -1, -1, -1, -1, -1,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 -1, -1, -1, -1, -1, -1, -1, -1,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x02, 0x02, -1, -1, 0x02, -1, -1, -1,
174 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
175 -1, -1, 0x02, -1, -1, -1, -1, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 -1, -1, -1, -1, -1, -1, -1, -1,
178 };
179
180
181 /*
182 * Define the private functions
183 */
184
185 /* for debugging */
186 #ifdef ADB_DEBUG
187 void pm_printerr __P((char *, int, int, char *));
188 #endif
189
190 int pm_wait_busy __P((int));
191 int pm_wait_free __P((int));
192
193 /* these functions are for the PB1XX series */
194 int pm_receive_pm1 __P((u_char *));
195 int pm_send_pm1 __P((u_char,int));
196 int pm_pmgrop_pm1 __P((PMData *));
197 int pm_intr_pm1 __P((void *));
198
199 /* these functions are for the PB Duo series and the PB 5XX series */
200 int pm_receive_pm2 __P((u_char *));
201 int pm_send_pm2 __P((u_char));
202 int pm_pmgrop_pm2 __P((PMData *));
203 int pm_intr_pm2 __P((void *));
204
205 /* these functions are called from adb_direct.c */
206 void pm_setup_adb __P((void));
207 void pm_check_adb_devices __P((int));
208 int pm_adb_op __P((u_char *, void *, void *, int));
209
210 /* these functions also use the variables of adb_direct.c */
211 void pm_adb_get_TALK_result __P((PMData *));
212 void pm_adb_get_ADB_data __P((PMData *));
213 void pm_adb_poll_next_device_pm1 __P((PMData *));
214
215
216 /*
217 * These variables are in adb_direct.c.
218 */
219 extern u_char *adbBuffer; /* pointer to user data area */
220 extern void *adbCompRout; /* pointer to the completion routine */
221 extern void *adbCompData; /* pointer to the completion routine data */
222 extern int adbWaiting; /* waiting for return data from the device */
223 extern int adbWaitingCmd; /* ADB command we are waiting for */
224 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */
225
226 #define ADB_MAX_MSG_LENGTH 16
227 #define ADB_MAX_HDR_LENGTH 8
228 struct adbCommand {
229 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */
230 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */
231 u_char *saveBuf; /* where to save result */
232 u_char *compRout; /* completion routine pointer */
233 u_char *compData; /* completion routine data pointer */
234 u_int cmd; /* the original command for this data */
235 u_int unsol; /* 1 if packet was unsolicited */
236 u_int ack_only; /* 1 for no special processing */
237 };
238 extern void adb_pass_up __P((struct adbCommand *));
239
240 #if 0
241 /*
242 * Define the external functions
243 */
244 extern int zshard __P((int)); /* from zs.c */
245 #endif
246
247 #ifdef ADB_DEBUG
248 /*
249 * This function dumps contents of the PMData
250 */
251 void
252 pm_printerr(ttl, rval, num, data)
253 char *ttl;
254 int rval;
255 int num;
256 char *data;
257 {
258 int i;
259
260 printf("pm: %s:%04x %02x ", ttl, rval, num);
261 for (i = 0; i < num; i++)
262 printf("%02x ", data[i]);
263 printf("\n");
264 }
265 #endif
266
267
268
269 /*
270 * Check the hardware type of the Power Manager
271 */
272 void
273 pm_setup_adb()
274 {
275 pmHardware = PM_HW_PB5XX; /* XXX */
276 }
277
278 static int
279 strinlist(char *targ, char *list, int listlen)
280 {
281 char *str;
282 int sl;
283
284 str = list;
285 while (listlen > 0) {
286 sl = strlen(str);
287 if (strncmp(targ, str, sl) == 0)
288 return 1;
289 str += sl+1;
290 listlen -= sl+1;
291 }
292 return 0;
293 }
294
295 /*
296 * Check the hardware type of the Power Manager
297 */
298 void
299 pm_init(void)
300 {
301 uint32_t regs[10];
302 PMData pmdata;
303 char compat[128];
304 int clen, node, imask;
305
306 node = OF_peer(0);
307 if (node == -1) {
308 printf("pmu: Failed to get root");
309 return;
310 }
311 clen = OF_getprop(node, "compatible", compat, sizeof(compat));
312 if (clen <= 0) {
313 printf("pmu: failed to read root compatible data %d\n", clen);
314 return;
315 }
316
317 imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
318
319 if (strinlist("AAPL,3500", compat, clen) ||
320 strinlist("AAPL,3400/2400", compat, clen)) {
321 /* How to distinguish BATT_COMET? */
322 pmu_nbatt = 1;
323 pmu_batt_type = BATT_HOOPER;
324 pmu_type = PMU_OHARE;
325 } else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
326 strinlist("PowerBook1,1", compat, clen)) {
327 pmu_nbatt = 2;
328 pmu_batt_type = BATT_SMART;
329 pmu_type = PMU_G3;
330 } else {
331 pmu_nbatt = 1;
332 pmu_batt_type = BATT_SMART;
333 pmu_type = PMU_KEYLARGO;
334 node = getnodebyname(0, "power-mgt");
335 if (node == -1) {
336 printf("pmu: can't find power-mgt\n");
337 return;
338 }
339 clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
340 if (clen < 24) {
341 printf("pmu: failed to read prim-info\n");
342 return;
343 }
344 pmu_nbatt = regs[6] >> 16;
345 }
346
347 pmdata.command = PMU_SET_IMASK;
348 pmdata.num_data = 1;
349 pmdata.s_buf = pmdata.data;
350 pmdata.r_buf = pmdata.data;
351 pmdata.data[0] = imask;
352 pmgrop(&pmdata);
353 }
354
355
356 /*
357 * Check the existent ADB devices
358 */
359 void
360 pm_check_adb_devices(id)
361 int id;
362 {
363 u_short ed = 0x1;
364
365 ed <<= id;
366 pm_existent_ADB_devices |= ed;
367 }
368
369
370 /*
371 * Wait until PM IC is busy
372 */
373 int
374 pm_wait_busy(delay)
375 int delay;
376 {
377 while (PM_IS_ON) {
378 #ifdef PM_GRAB_SI
379 #if 0
380 zshard(0); /* grab any serial interrupts */
381 #else
382 (void)intr_dispatch(0x70);
383 #endif
384 #endif
385 if ((--delay) < 0)
386 return 1; /* timeout */
387 }
388 return 0;
389 }
390
391
392 /*
393 * Wait until PM IC is free
394 */
395 int
396 pm_wait_free(delay)
397 int delay;
398 {
399 while (PM_IS_OFF) {
400 #ifdef PM_GRAB_SI
401 #if 0
402 zshard(0); /* grab any serial interrupts */
403 #else
404 (void)intr_dispatch(0x70);
405 #endif
406 #endif
407 if ((--delay) < 0)
408 return 0; /* timeout */
409 }
410 return 1;
411 }
412
413
414
415 /*
416 * Functions for the PB1XX series
417 */
418
419 /*
420 * Receive data from PM for the PB1XX series
421 */
422 int
423 pm_receive_pm1(data)
424 u_char *data;
425 {
426 #if 0
427 int rval = 0xffffcd34;
428
429 via_reg(VIA2, vDirA) = 0x00;
430
431 switch (1) {
432 default:
433 if (pm_wait_busy(0x40) != 0)
434 break; /* timeout */
435
436 PM_SET_STATE_ACKOFF();
437 *data = via_reg(VIA2, 0x200);
438
439 rval = 0xffffcd33;
440 if (pm_wait_free(0x40) == 0)
441 break; /* timeout */
442
443 rval = 0x00;
444 break;
445 }
446
447 PM_SET_STATE_ACKON();
448 via_reg(VIA2, vDirA) = 0x00;
449
450 return rval;
451 #else
452 panic("pm_receive_pm1");
453 #endif
454 }
455
456
457
458 /*
459 * Send data to PM for the PB1XX series
460 */
461 int
462 pm_send_pm1(data, delay)
463 u_char data;
464 int delay;
465 {
466 #if 0
467 int rval;
468
469 via_reg(VIA2, vDirA) = 0xff;
470 via_reg(VIA2, 0x200) = data;
471
472 PM_SET_STATE_ACKOFF();
473 if (pm_wait_busy(0x400) != 0) {
474 PM_SET_STATE_ACKON();
475 via_reg(VIA2, vDirA) = 0x00;
476
477 return 0xffffcd36;
478 }
479
480 rval = 0x0;
481 PM_SET_STATE_ACKON();
482 if (pm_wait_free(0x40) == 0)
483 rval = 0xffffcd35;
484
485 PM_SET_STATE_ACKON();
486 via_reg(VIA2, vDirA) = 0x00;
487
488 return rval;
489 #else
490 panic("pm_send_pm1");
491 #endif
492 }
493
494
495 /*
496 * My PMgrOp routine for the PB1XX series
497 */
498 int
499 pm_pmgrop_pm1(pmdata)
500 PMData *pmdata;
501 {
502 #if 0
503 int i;
504 int s = 0x81815963;
505 u_char via1_vIER, via1_vDirA;
506 int rval = 0;
507 int num_pm_data = 0;
508 u_char pm_cmd;
509 u_char pm_data;
510 u_char *pm_buf;
511
512 /* disable all inetrrupts but PM */
513 via1_vIER = via_reg(VIA1, vIER);
514 PM_VIA_INTR_DISABLE();
515
516 via1_vDirA = via_reg(VIA1, vDirA);
517
518 switch (pmdata->command) {
519 default:
520 for (i = 0; i < 7; i++) {
521 via_reg(VIA2, vDirA) = 0x00;
522
523 /* wait until PM is free */
524 if (pm_wait_free(ADBDelay) == 0) { /* timeout */
525 via_reg(VIA2, vDirA) = 0x00;
526 /* restore formar value */
527 via_reg(VIA1, vDirA) = via1_vDirA;
528 via_reg(VIA1, vIER) = via1_vIER;
529 return 0xffffcd38;
530 }
531
532 switch (mac68k_machine.machineid) {
533 case MACH_MACPB160:
534 case MACH_MACPB165:
535 case MACH_MACPB165C:
536 case MACH_MACPB180:
537 case MACH_MACPB180C:
538 {
539 int delay = ADBDelay * 16;
540
541 via_reg(VIA2, vDirA) = 0x00;
542 while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0))
543 delay--;
544
545 if (delay < 0) { /* timeout */
546 via_reg(VIA2, vDirA) = 0x00;
547 /* restore formar value */
548 via_reg(VIA1, vIER) = via1_vIER;
549 return 0xffffcd38;
550 }
551 }
552 } /* end switch */
553
554 s = splhigh();
555
556 via1_vDirA = via_reg(VIA1, vDirA);
557 via_reg(VIA1, vDirA) &= 0x7f;
558
559 pm_cmd = (u_char)(pmdata->command & 0xff);
560 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0)
561 break; /* send command succeeded */
562
563 via_reg(VIA1, vDirA) = via1_vDirA;
564 splx(s);
565 } /* end for */
566
567 /* failed to send a command */
568 if (i == 7) {
569 via_reg(VIA2, vDirA) = 0x00;
570 /* restore formar value */
571 via_reg(VIA1, vDirA) = via1_vDirA;
572 via_reg(VIA1, vIER) = via1_vIER;
573 if (s != 0x81815963)
574 splx(s);
575 return 0xffffcd38;
576 }
577
578 /* send # of PM data */
579 num_pm_data = pmdata->num_data;
580 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0)
581 break; /* timeout */
582
583 /* send PM data */
584 pm_buf = (u_char *)pmdata->s_buf;
585 for (i = 0; i < num_pm_data; i++)
586 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0)
587 break; /* timeout */
588 if ((i != num_pm_data) && (num_pm_data != 0))
589 break; /* timeout */
590
591 /* Will PM IC return data? */
592 if ((pm_cmd & 0x08) == 0) {
593 rval = 0;
594 break; /* no returned data */
595 }
596
597 rval = 0xffffcd37;
598 if (pm_wait_busy(ADBDelay) != 0)
599 break; /* timeout */
600
601 /* receive PM command */
602 if ((rval = pm_receive_pm1(&pm_data)) != 0)
603 break;
604
605 pmdata->command = pm_data;
606
607 /* receive number of PM data */
608 if ((rval = pm_receive_pm1(&pm_data)) != 0)
609 break; /* timeout */
610 num_pm_data = pm_data;
611 pmdata->num_data = num_pm_data;
612
613 /* receive PM data */
614 pm_buf = (u_char *)pmdata->r_buf;
615 for (i = 0; i < num_pm_data; i++) {
616 if ((rval = pm_receive_pm1(&pm_data)) != 0)
617 break; /* timeout */
618 pm_buf[i] = pm_data;
619 }
620
621 rval = 0;
622 }
623
624 via_reg(VIA2, vDirA) = 0x00;
625
626 /* restore formar value */
627 via_reg(VIA1, vDirA) = via1_vDirA;
628 via_reg(VIA1, vIER) = via1_vIER;
629 if (s != 0x81815963)
630 splx(s);
631
632 return rval;
633 #else
634 panic("pm_pmgrop_pm1");
635 #endif
636 }
637
638
639 /*
640 * My PM interrupt routine for PB1XX series
641 */
642 int
643 pm_intr_pm1(void *arg)
644 {
645 #if 0
646 int s;
647 int rval;
648 PMData pmdata;
649
650 s = splhigh();
651
652 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
653
654 /* ask PM what happend */
655 pmdata.command = PMU_INT_ACK;
656 pmdata.num_data = 0;
657 pmdata.data[0] = pmdata.data[1] = 0;
658 pmdata.s_buf = &pmdata.data[2];
659 pmdata.r_buf = &pmdata.data[2];
660 rval = pm_pmgrop_pm1(&pmdata);
661 if (rval != 0) {
662 #ifdef ADB_DEBUG
663 if (adb_debug)
664 printf("pm: PM is not ready. error code=%08x\n", rval);
665 #endif
666 splx(s);
667 }
668
669 if ((pmdata.data[2] & 0x10) == 0x10) {
670 if ((pmdata.data[2] & 0x0f) == 0) {
671 /* ADB data that were requested by TALK command */
672 pm_adb_get_TALK_result(&pmdata);
673 } else if ((pmdata.data[2] & 0x08) == 0x8) {
674 /* PM is requesting to poll */
675 pm_adb_poll_next_device_pm1(&pmdata);
676 } else if ((pmdata.data[2] & 0x04) == 0x4) {
677 /* ADB device event */
678 pm_adb_get_ADB_data(&pmdata);
679 }
680 } else {
681 #ifdef ADB_DEBUG
682 if (adb_debug)
683 pm_printerr("driver does not support this event.",
684 rval, pmdata.num_data, pmdata.data);
685 #endif
686 }
687
688 splx(s);
689 #else
690 panic("pm_intr_pm1");
691 #endif
692 return 1;
693 }
694
695
696
697 /*
698 * Functions for the PB Duo series and the PB 5XX series
699 */
700
701 /*
702 * Receive data from PM for the PB Duo series and the PB 5XX series
703 */
704 int
705 pm_receive_pm2(data)
706 u_char *data;
707 {
708 int i;
709 int rval;
710
711 rval = 0xffffcd34;
712
713 switch (1) {
714 default:
715 /* set VIA SR to input mode */
716 via_reg_or(VIA1, vACR, 0x0c);
717 via_reg_and(VIA1, vACR, ~0x10);
718 i = PM_SR();
719
720 PM_SET_STATE_ACKOFF();
721 if (pm_wait_busy((int)ADBDelay*32) != 0)
722 break; /* timeout */
723
724 PM_SET_STATE_ACKON();
725 rval = 0xffffcd33;
726 if (pm_wait_free((int)ADBDelay*32) == 0)
727 break; /* timeout */
728
729 *data = PM_SR();
730 rval = 0;
731
732 break;
733 }
734
735 PM_SET_STATE_ACKON();
736 via_reg_or(VIA1, vACR, 0x1c);
737
738 return rval;
739 }
740
741
742
743 /*
744 * Send data to PM for the PB Duo series and the PB 5XX series
745 */
746 int
747 pm_send_pm2(data)
748 u_char data;
749 {
750 int rval;
751
752 via_reg_or(VIA1, vACR, 0x1c);
753 write_via_reg(VIA1, vSR, data); /* PM_SR() = data; */
754
755 PM_SET_STATE_ACKOFF();
756 rval = 0xffffcd36;
757 if (pm_wait_busy((int)ADBDelay*32) != 0) {
758 PM_SET_STATE_ACKON();
759
760 via_reg_or(VIA1, vACR, 0x1c);
761
762 return rval;
763 }
764
765 PM_SET_STATE_ACKON();
766 rval = 0xffffcd35;
767 if (pm_wait_free((int)ADBDelay*32) != 0)
768 rval = 0;
769
770 PM_SET_STATE_ACKON();
771 via_reg_or(VIA1, vACR, 0x1c);
772
773 return rval;
774 }
775
776
777
778 /*
779 * My PMgrOp routine for the PB Duo series and the PB 5XX series
780 */
781 int
782 pm_pmgrop_pm2(pmdata)
783 PMData *pmdata;
784 {
785 int i;
786 int s;
787 u_char via1_vIER;
788 int rval = 0;
789 int num_pm_data = 0;
790 u_char pm_cmd;
791 short pm_num_rx_data;
792 u_char pm_data;
793 u_char *pm_buf;
794
795 s = splhigh();
796
797 /* disable all inetrrupts but PM */
798 via1_vIER = 0x10;
799 via1_vIER &= read_via_reg(VIA1, vIER);
800 write_via_reg(VIA1, vIER, via1_vIER);
801 if (via1_vIER != 0x0)
802 via1_vIER |= 0x80;
803
804 switch (pmdata->command) {
805 default:
806 /* wait until PM is free */
807 pm_cmd = (u_char)(pmdata->command & 0xff);
808 rval = 0xcd38;
809 if (pm_wait_free(ADBDelay * 4) == 0)
810 break; /* timeout */
811
812 if (HwCfgFlags3 & 0x00200000) {
813 /* PB 160, PB 165(c), PB 180(c)? */
814 int delay = ADBDelay * 16;
815
816 write_via_reg(VIA2, vDirA, 0x00);
817 while ((read_via_reg(VIA2, 0x200) == 0x07) &&
818 (delay >= 0))
819 delay--;
820
821 if (delay < 0) {
822 rval = 0xffffcd38;
823 break; /* timeout */
824 }
825 }
826
827 /* send PM command */
828 if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff))))
829 break; /* timeout */
830
831 /* send number of PM data */
832 num_pm_data = pmdata->num_data;
833 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
834 if (pm_send_cmd_type[pm_cmd] < 0) {
835 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
836 break; /* timeout */
837 pmdata->command = 0;
838 }
839 } else { /* PB 1XX series ? */
840 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0)
841 break; /* timeout */
842 }
843 /* send PM data */
844 pm_buf = (u_char *)pmdata->s_buf;
845 for (i = 0 ; i < num_pm_data; i++)
846 if ((rval = pm_send_pm2(pm_buf[i])) != 0)
847 break; /* timeout */
848 if (i != num_pm_data)
849 break; /* timeout */
850
851
852 /* check if PM will send me data */
853 pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
854 pmdata->num_data = pm_num_rx_data;
855 if (pm_num_rx_data == 0) {
856 rval = 0;
857 break; /* no return data */
858 }
859
860 /* receive PM command */
861 pm_data = pmdata->command;
862 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
863 pm_num_rx_data--;
864 if (pm_num_rx_data == 0)
865 if ((rval = pm_receive_pm2(&pm_data)) != 0) {
866 rval = 0xffffcd37;
867 break;
868 }
869 pmdata->command = pm_data;
870 } else { /* PB 1XX series ? */
871 if ((rval = pm_receive_pm2(&pm_data)) != 0) {
872 rval = 0xffffcd37;
873 break;
874 }
875 pmdata->command = pm_data;
876 }
877
878 /* receive number of PM data */
879 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */
880 if (pm_num_rx_data < 0) {
881 if ((rval = pm_receive_pm2(&pm_data)) != 0)
882 break; /* timeout */
883 num_pm_data = pm_data;
884 } else
885 num_pm_data = pm_num_rx_data;
886 pmdata->num_data = num_pm_data;
887 } else { /* PB 1XX serias ? */
888 if ((rval = pm_receive_pm2(&pm_data)) != 0)
889 break; /* timeout */
890 num_pm_data = pm_data;
891 pmdata->num_data = num_pm_data;
892 }
893
894 /* receive PM data */
895 pm_buf = (u_char *)pmdata->r_buf;
896 for (i = 0; i < num_pm_data; i++) {
897 if ((rval = pm_receive_pm2(&pm_data)) != 0)
898 break; /* timeout */
899 pm_buf[i] = pm_data;
900 }
901
902 rval = 0;
903 }
904
905 /* restore former value */
906 write_via_reg(VIA1, vIER, via1_vIER);
907 splx(s);
908
909 return rval;
910 }
911
912
913 /*
914 * My PM interrupt routine for the PB Duo series and the PB 5XX series
915 */
916 int
917 pm_intr_pm2(void *arg)
918 {
919 int s;
920 int rval;
921 PMData pmdata;
922
923 s = splhigh();
924
925 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
926 /* ask PM what happend */
927 pmdata.command = PMU_INT_ACK;
928 pmdata.num_data = 0;
929 pmdata.s_buf = &pmdata.data[2];
930 pmdata.r_buf = &pmdata.data[2];
931 rval = pm_pmgrop_pm2(&pmdata);
932 if (rval != 0) {
933 #ifdef ADB_DEBUG
934 if (adb_debug)
935 printf("pm: PM is not ready. error code: %08x\n", rval);
936 #endif
937 splx(s);
938 return 0;
939 }
940
941 switch ((u_int)(pmdata.data[2] & 0xff)) {
942 case 0x00: /* no event pending? */
943 break;
944 case 0x80: /* 1 sec interrupt? */
945 pm_counter++;
946 break;
947 case 0x08: /* Brightness/Contrast button on LCD panel */
948 /* get brightness and contrast of the LCD */
949 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
950 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
951 /*
952 pm_printerr("#08", rval, pmdata.num_data, pmdata.data);
953 pmdata.command = 0x33;
954 pmdata.num_data = 1;
955 pmdata.s_buf = pmdata.data;
956 pmdata.r_buf = pmdata.data;
957 pmdata.data[0] = pm_LCD_contrast;
958 rval = pm_pmgrop_pm2(&pmdata);
959 pm_printerr("#33", rval, pmdata.num_data, pmdata.data);
960 */
961 /* this is an experimental code */
962 pmdata.command = PMU_SET_BRIGHTNESS;
963 pmdata.num_data = 1;
964 pmdata.s_buf = pmdata.data;
965 pmdata.r_buf = pmdata.data;
966 pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
967 if (pm_LCD_brightness < 0x08)
968 pm_LCD_brightness = 0x08;
969 if (pm_LCD_brightness > 0x78)
970 pm_LCD_brightness = 0x78;
971 pmdata.data[0] = pm_LCD_brightness;
972 rval = pm_pmgrop_pm2(&pmdata);
973 break;
974 case 0x10: /* ADB data requested by TALK command */
975 case 0x14:
976 pm_adb_get_TALK_result(&pmdata);
977 break;
978 case 0x16: /* ADB device event */
979 case 0x18:
980 case 0x1e:
981 pm_adb_get_ADB_data(&pmdata);
982 break;
983 default:
984 #ifdef ADB_DEBUG
985 if (adb_debug)
986 pm_printerr("driver does not support this event.",
987 pmdata.data[2], pmdata.num_data,
988 pmdata.data);
989 #endif
990 break;
991 }
992
993 splx(s);
994
995 return 1;
996 }
997
998
999 /*
1000 * My PMgrOp routine
1001 */
1002 int
1003 pmgrop(pmdata)
1004 PMData *pmdata;
1005 {
1006 switch (pmHardware) {
1007 case PM_HW_PB1XX:
1008 return (pm_pmgrop_pm1(pmdata));
1009 break;
1010 case PM_HW_PB5XX:
1011 return (pm_pmgrop_pm2(pmdata));
1012 break;
1013 default:
1014 /* return (pmgrop_mrg(pmdata)); */
1015 return 1;
1016 }
1017 }
1018
1019
1020 /*
1021 * My PM interrupt routine
1022 */
1023 int
1024 pm_intr(void *arg)
1025 {
1026 switch (pmHardware) {
1027 case PM_HW_PB1XX:
1028 return pm_intr_pm1(arg);
1029 break;
1030 case PM_HW_PB5XX:
1031 return pm_intr_pm2(arg);
1032 break;
1033 default:
1034 break;
1035 }
1036 return 0;
1037 }
1038
1039
1040
1041 /*
1042 * Synchronous ADBOp routine for the Power Manager
1043 */
1044 int
1045 pm_adb_op(buffer, compRout, data, command)
1046 u_char *buffer;
1047 void *compRout;
1048 void *data;
1049 int command;
1050 {
1051 int i;
1052 int s;
1053 int rval;
1054 int timo;
1055 PMData pmdata;
1056 struct adbCommand packet;
1057
1058 if (adbWaiting == 1)
1059 return 1;
1060
1061 s = splhigh();
1062 write_via_reg(VIA1, vIER, 0x10);
1063
1064 adbBuffer = buffer;
1065 adbCompRout = compRout;
1066 adbCompData = data;
1067
1068 pmdata.command = PMU_ADB_CMD;
1069 pmdata.s_buf = pmdata.data;
1070 pmdata.r_buf = pmdata.data;
1071
1072 /* if the command is LISTEN, add number of ADB data to number of PM data */
1073 if ((command & 0xc) == 0x8) {
1074 if (buffer != (u_char *)0)
1075 pmdata.num_data = buffer[0] + 3;
1076 } else {
1077 pmdata.num_data = 3;
1078 }
1079
1080 pmdata.data[0] = (u_char)(command & 0xff);
1081 pmdata.data[1] = 0;
1082 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */
1083 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
1084 pmdata.data[2] = buffer[0]; /* number of data */
1085 for (i = 0; i < buffer[0]; i++)
1086 pmdata.data[3 + i] = buffer[1 + i];
1087 } else
1088 pmdata.data[2] = 0;
1089 } else
1090 pmdata.data[2] = 0;
1091
1092 if ((command & 0xc) != 0xc) { /* if the command is not TALK */
1093 /* set up stuff for adb_pass_up */
1094 packet.data[0] = 1 + pmdata.data[2];
1095 packet.data[1] = command;
1096 for (i = 0; i < pmdata.data[2]; i++)
1097 packet.data[i+2] = pmdata.data[i+3];
1098 packet.saveBuf = adbBuffer;
1099 packet.compRout = adbCompRout;
1100 packet.compData = adbCompData;
1101 packet.cmd = command;
1102 packet.unsol = 0;
1103 packet.ack_only = 1;
1104 adb_polling = 1;
1105 adb_pass_up(&packet);
1106 adb_polling = 0;
1107 }
1108
1109 rval = pmgrop(&pmdata);
1110 if (rval != 0) {
1111 splx(s);
1112 return 1;
1113 }
1114
1115 delay(10000);
1116
1117 adbWaiting = 1;
1118 adbWaitingCmd = command;
1119
1120 PM_VIA_INTR_ENABLE();
1121
1122 /* wait until the PM interrupt has occurred */
1123 timo = 0x80000;
1124 while (adbWaiting == 1) {
1125 if (read_via_reg(VIA1, vIFR) & 0x14)
1126 pm_intr(NULL);
1127 #ifdef PM_GRAB_SI
1128 #if 0
1129 zshard(0); /* grab any serial interrupts */
1130 #else
1131 (void)intr_dispatch(0x70);
1132 #endif
1133 #endif
1134 if ((--timo) < 0) {
1135 /* Try to take an interrupt anyway, just in case.
1136 * This has been observed to happen on my ibook
1137 * when i press a key after boot and before adb
1138 * is attached; For example, when booting with -d.
1139 */
1140 pm_intr(NULL);
1141 if (adbWaiting) {
1142 printf("pm_adb_op: timeout. command = 0x%x\n",command);
1143 splx(s);
1144 return 1;
1145 }
1146 #ifdef ADB_DEBUG
1147 else {
1148 printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
1149 }
1150 #endif
1151 }
1152 }
1153
1154 /* this command enables the interrupt by operating ADB devices */
1155 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */
1156 pmdata.command = PMU_ADB_CMD;
1157 pmdata.num_data = 4;
1158 pmdata.s_buf = pmdata.data;
1159 pmdata.r_buf = pmdata.data;
1160 pmdata.data[0] = 0x00;
1161 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */
1162 pmdata.data[2] = 0x00;
1163 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */
1164 } else { /* PB 1XX series */
1165 pmdata.command = PMU_ADB_CMD;
1166 pmdata.num_data = 3;
1167 pmdata.s_buf = pmdata.data;
1168 pmdata.r_buf = pmdata.data;
1169 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc;
1170 pmdata.data[1] = 0x04;
1171 pmdata.data[2] = 0x00;
1172 }
1173 rval = pmgrop(&pmdata);
1174
1175 splx(s);
1176 return rval;
1177 }
1178
1179
1180 void
1181 pm_adb_get_TALK_result(pmdata)
1182 PMData *pmdata;
1183 {
1184 int i;
1185 struct adbCommand packet;
1186
1187 /* set up data for adb_pass_up */
1188 packet.data[0] = pmdata->num_data-1;
1189 packet.data[1] = pmdata->data[3];
1190 for (i = 0; i <packet.data[0]-1; i++)
1191 packet.data[i+2] = pmdata->data[i+4];
1192
1193 packet.saveBuf = adbBuffer;
1194 packet.compRout = adbCompRout;
1195 packet.compData = adbCompData;
1196 packet.unsol = 0;
1197 packet.ack_only = 0;
1198 adb_polling = 1;
1199 adb_pass_up(&packet);
1200 adb_polling = 0;
1201
1202 adbWaiting = 0;
1203 adbBuffer = (long)0;
1204 adbCompRout = (long)0;
1205 adbCompData = (long)0;
1206 }
1207
1208
1209 void
1210 pm_adb_get_ADB_data(pmdata)
1211 PMData *pmdata;
1212 {
1213 int i;
1214 struct adbCommand packet;
1215
1216 if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
1217 pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
1218 ((pmdata->data[2] & ~1) == 0xf4)) {
1219 if (pmdata->data[2] == 0xf4) {
1220 pm_eject_pcmcia(0);
1221 } else {
1222 pm_eject_pcmcia(1);
1223 }
1224 return;
1225 }
1226 /* set up data for adb_pass_up */
1227 packet.data[0] = pmdata->num_data-1; /* number of raw data */
1228 packet.data[1] = pmdata->data[3]; /* ADB command */
1229 for (i = 0; i <packet.data[0]-1; i++)
1230 packet.data[i+2] = pmdata->data[i+4];
1231 packet.unsol = 1;
1232 packet.ack_only = 0;
1233 adb_pass_up(&packet);
1234 }
1235
1236
1237 void
1238 pm_adb_poll_next_device_pm1(pmdata)
1239 PMData *pmdata;
1240 {
1241 int i;
1242 int ndid;
1243 u_short bendid = 0x1;
1244 int rval;
1245 PMData tmp_pmdata;
1246
1247 /* find another existent ADB device to poll */
1248 for (i = 1; i < 16; i++) {
1249 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf;
1250 bendid <<= ndid;
1251 if ((pm_existent_ADB_devices & bendid) != 0)
1252 break;
1253 }
1254
1255 /* poll the other device */
1256 tmp_pmdata.command = PMU_ADB_CMD;
1257 tmp_pmdata.num_data = 3;
1258 tmp_pmdata.s_buf = tmp_pmdata.data;
1259 tmp_pmdata.r_buf = tmp_pmdata.data;
1260 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc;
1261 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */
1262 tmp_pmdata.data[2] = 0x00;
1263 rval = pmgrop(&tmp_pmdata);
1264 }
1265
1266 void
1267 pm_adb_restart()
1268 {
1269 PMData p;
1270
1271 p.command = PMU_RESET_CPU;
1272 p.num_data = 0;
1273 p.s_buf = p.data;
1274 p.r_buf = p.data;
1275 pmgrop(&p);
1276 }
1277
1278 void
1279 pm_adb_poweroff()
1280 {
1281 PMData p;
1282
1283 p.command = PMU_POWER_OFF;
1284 p.num_data = 4;
1285 p.s_buf = p.data;
1286 p.r_buf = p.data;
1287 strcpy(p.data, "MATT");
1288 pmgrop(&p);
1289 }
1290
1291 void
1292 pm_read_date_time(time)
1293 u_long *time;
1294 {
1295 PMData p;
1296
1297 p.command = PMU_READ_RTC;
1298 p.num_data = 0;
1299 p.s_buf = p.data;
1300 p.r_buf = p.data;
1301 pmgrop(&p);
1302
1303 memcpy(time, p.data, 4);
1304 }
1305
1306 void
1307 pm_set_date_time(time)
1308 u_long time;
1309 {
1310 PMData p;
1311
1312 p.command = PMU_SET_RTC;
1313 p.num_data = 4;
1314 p.s_buf = p.r_buf = p.data;
1315 memcpy(p.data, &time, 4);
1316 pmgrop(&p);
1317 }
1318
1319 int
1320 pm_read_brightness()
1321 {
1322 PMData p;
1323
1324 p.command = PMU_READ_BRIGHTNESS;
1325 p.num_data = 1; /* XXX why 1? */
1326 p.s_buf = p.r_buf = p.data;
1327 p.data[0] = 0;
1328 pmgrop(&p);
1329
1330 return p.data[0];
1331 }
1332
1333 void
1334 pm_set_brightness(val)
1335 int val;
1336 {
1337 PMData p;
1338
1339 val = 0x7f - val / 2;
1340 if (val < 0x08)
1341 val = 0x08;
1342 if (val > 0x78)
1343 val = 0x78;
1344
1345 p.command = PMU_SET_BRIGHTNESS;
1346 p.num_data = 1;
1347 p.s_buf = p.r_buf = p.data;
1348 p.data[0] = val;
1349 pmgrop(&p);
1350 }
1351
1352 void
1353 pm_init_brightness()
1354 {
1355 int val;
1356
1357 val = pm_read_brightness();
1358 pm_set_brightness(val);
1359 }
1360
1361 void
1362 pm_eject_pcmcia(slot)
1363 int slot;
1364 {
1365 PMData p;
1366
1367 if (slot != 0 && slot != 1)
1368 return;
1369
1370 p.command = PMU_EJECT_PCMCIA;
1371 p.num_data = 1;
1372 p.s_buf = p.r_buf = p.data;
1373 p.data[0] = 5 + slot; /* XXX */
1374 pmgrop(&p);
1375 }
1376
1377 /*
1378 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
1379 * for a clear description of the PMU results.
1380 */
1381 static int
1382 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
1383 {
1384 PMData p;
1385
1386 p.command = PMU_SMART_BATTERY_STATE;
1387 p.num_data = 1;
1388 p.s_buf = p.r_buf = p.data;
1389 p.data[0] = battery + 1;
1390 pmgrop(&p);
1391
1392 info->flags = p.data[1];
1393
1394 info->secs_remaining = 0;
1395 switch (p.data[0]) {
1396 case 3:
1397 case 4:
1398 info->cur_charge = p.data[2];
1399 info->max_charge = p.data[3];
1400 info->draw = *((signed char *)&p.data[4]);
1401 info->voltage = p.data[5];
1402 break;
1403 case 5:
1404 info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
1405 info->max_charge = ((p.data[4] << 8) | (p.data[5]));
1406 info->draw = *((signed short *)&p.data[6]);
1407 info->voltage = ((p.data[8] << 8) | (p.data[7]));
1408 break;
1409 default:
1410 /* XXX - Error condition */
1411 info->cur_charge = 0;
1412 info->max_charge = 0;
1413 info->draw = 0;
1414 info->voltage = 0;
1415 break;
1416 }
1417 if (info->draw) {
1418 if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
1419 info->secs_remaining =
1420 ((info->max_charge - info->cur_charge) * 3600)
1421 / info->draw;
1422 } else {
1423 info->secs_remaining =
1424 (info->cur_charge * 3600) / -info->draw;
1425 }
1426 }
1427
1428 return 1;
1429 }
1430
1431 static int
1432 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
1433 {
1434 PMData p;
1435 long pcharge=0, charge, vb, vmax, lmax;
1436 long vmax_charging, vmax_charged, amperage, voltage;
1437
1438 p.command = PMU_BATTERY_STATE;
1439 p.num_data = 0;
1440 p.s_buf = p.r_buf = p.data;
1441 pmgrop(&p);
1442
1443 info->flags = p.data[0];
1444
1445 if (info->flags & PMU_PWR_BATT_PRESENT) {
1446 if (ty == BATT_COMET) {
1447 vmax_charging = 213;
1448 vmax_charged = 189;
1449 lmax = 6500;
1450 } else {
1451 /* Experimental values */
1452 vmax_charging = 365;
1453 vmax_charged = 365;
1454 lmax = 6500;
1455 }
1456 vmax = vmax_charged;
1457 vb = (p.data[1] << 8) | p.data[2];
1458 voltage = (vb * 256 + 72665) / 10;
1459 amperage = (unsigned char) p.data[5];
1460 if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1461 if (amperage > 200)
1462 vb += ((amperage - 200) * 15)/100;
1463 } else if (info->flags & PMU_PWR_BATT_CHARGING) {
1464 vb = (vb * 97) / 100;
1465 vmax = vmax_charging;
1466 }
1467 charge = (100 * vb) / vmax;
1468 if (info->flags & PMU_PWR_PCHARGE_RESET) {
1469 pcharge = (p.data[6] << 8) | p.data[7];
1470 if (pcharge > lmax)
1471 pcharge = lmax;
1472 pcharge *= 100;
1473 pcharge = 100 - pcharge / lmax;
1474 if (pcharge < charge)
1475 charge = pcharge;
1476 }
1477 info->cur_charge = charge;
1478 info->max_charge = 100;
1479 info->draw = -amperage;
1480 info->voltage = voltage;
1481 if (amperage > 0)
1482 info->secs_remaining = (charge * 16440) / amperage;
1483 else
1484 info->secs_remaining = 0;
1485 } else {
1486 info->cur_charge = 0;
1487 info->max_charge = 0;
1488 info->draw = 0;
1489 info->voltage = 0;
1490 info->secs_remaining = 0;
1491 }
1492
1493 return 1;
1494 }
1495
1496 int
1497 pm_battery_info(int battery, struct pmu_battery_info *info)
1498 {
1499
1500 if (battery > pmu_nbatt)
1501 return 0;
1502
1503 switch (pmu_batt_type) {
1504 case BATT_COMET:
1505 case BATT_HOOPER:
1506 return pm_battery_info_legacy(battery, info, pmu_batt_type);
1507
1508 case BATT_SMART:
1509 return pm_battery_info_smart(battery, info);
1510 }
1511
1512 return 0;
1513 }
1514
1515 int
1516 pm_read_nvram(addr)
1517 int addr;
1518 {
1519 PMData p;
1520
1521 p.command = PMU_READ_NVRAM;
1522 p.num_data = 2;
1523 p.s_buf = p.r_buf = p.data;
1524 p.data[0] = addr >> 8;
1525 p.data[1] = addr;
1526 pmgrop(&p);
1527
1528 return p.data[0];
1529 }
1530
1531 void
1532 pm_write_nvram(addr, val)
1533 int addr, val;
1534 {
1535 PMData p;
1536
1537 p.command = PMU_WRITE_NVRAM;
1538 p.num_data = 3;
1539 p.s_buf = p.r_buf = p.data;
1540 p.data[0] = addr >> 8;
1541 p.data[1] = addr;
1542 p.data[2] = val;
1543 pmgrop(&p);
1544 }
1545