pm_direct.c revision 1.23 1 /* $NetBSD: pm_direct.c,v 1.23 2005/02/01 02:46:00 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.23 2005/02/01 02:46:00 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 void 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 void 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 void pm_intr __P((void));
209 int pm_adb_op __P((u_char *, void *, void *, int));
210
211 /* these functions also use the variables of adb_direct.c */
212 void pm_adb_get_TALK_result __P((PMData *));
213 void pm_adb_get_ADB_data __P((PMData *));
214 void pm_adb_poll_next_device_pm1 __P((PMData *));
215
216
217 /*
218 * These variables are in adb_direct.c.
219 */
220 extern u_char *adbBuffer; /* pointer to user data area */
221 extern void *adbCompRout; /* pointer to the completion routine */
222 extern void *adbCompData; /* pointer to the completion routine data */
223 extern int adbWaiting; /* waiting for return data from the device */
224 extern int adbWaitingCmd; /* ADB command we are waiting for */
225 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */
226
227 #define ADB_MAX_MSG_LENGTH 16
228 #define ADB_MAX_HDR_LENGTH 8
229 struct adbCommand {
230 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */
231 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */
232 u_char *saveBuf; /* where to save result */
233 u_char *compRout; /* completion routine pointer */
234 u_char *compData; /* completion routine data pointer */
235 u_int cmd; /* the original command for this data */
236 u_int unsol; /* 1 if packet was unsolicited */
237 u_int ack_only; /* 1 for no special processing */
238 };
239 extern void adb_pass_up __P((struct adbCommand *));
240
241 #if 0
242 /*
243 * Define the external functions
244 */
245 extern int zshard __P((int)); /* from zs.c */
246 #endif
247
248 #ifdef ADB_DEBUG
249 /*
250 * This function dumps contents of the PMData
251 */
252 void
253 pm_printerr(ttl, rval, num, data)
254 char *ttl;
255 int rval;
256 int num;
257 char *data;
258 {
259 int i;
260
261 printf("pm: %s:%04x %02x ", ttl, rval, num);
262 for (i = 0; i < num; i++)
263 printf("%02x ", data[i]);
264 printf("\n");
265 }
266 #endif
267
268
269
270 /*
271 * Check the hardware type of the Power Manager
272 */
273 void
274 pm_setup_adb()
275 {
276 pmHardware = PM_HW_PB5XX; /* XXX */
277 }
278
279 static int
280 strinlist(char *targ, char *list, int listlen)
281 {
282 char *str;
283 int sl;
284
285 str = list;
286 while (listlen > 0) {
287 sl = strlen(str);
288 if (strncmp(targ, str, sl) == 0)
289 return 1;
290 str += sl+1;
291 listlen -= sl+1;
292 }
293 return 0;
294 }
295
296 /*
297 * Check the hardware type of the Power Manager
298 */
299 void
300 pm_init(void)
301 {
302 uint32_t regs[10];
303 PMData pmdata;
304 char compat[128];
305 int clen, node, imask;
306
307 node = OF_peer(0);
308 if (node == -1) {
309 printf("pmu: Failed to get root");
310 return;
311 }
312 clen = OF_getprop(node, "compatible", compat, sizeof(compat));
313 if (clen <= 0) {
314 printf("pmu: failed to read root compatible data %d\n", clen);
315 return;
316 }
317
318 imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
319
320 if (strinlist("AAPL,3500", compat, clen) ||
321 strinlist("AAPL,3400/2400", compat, clen)) {
322 /* How to distinguish BATT_COMET? */
323 pmu_nbatt = 1;
324 pmu_batt_type = BATT_HOOPER;
325 pmu_type = PMU_OHARE;
326 } else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
327 strinlist("PowerBook1,1", compat, clen)) {
328 pmu_nbatt = 2;
329 pmu_batt_type = BATT_SMART;
330 pmu_type = PMU_G3;
331 } else {
332 pmu_nbatt = 1;
333 pmu_batt_type = BATT_SMART;
334 pmu_type = PMU_KEYLARGO;
335 node = getnodebyname(0, "power-mgt");
336 if (node == -1) {
337 printf("pmu: can't find power-mgt\n");
338 return;
339 }
340 clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
341 if (clen < 24) {
342 printf("pmu: failed to read prim-info\n");
343 return;
344 }
345 pmu_nbatt = regs[6] >> 16;
346 }
347
348 pmdata.command = PMU_SET_IMASK;
349 pmdata.num_data = 1;
350 pmdata.s_buf = pmdata.data;
351 pmdata.r_buf = pmdata.data;
352 pmdata.data[0] = imask;
353 pmgrop(&pmdata);
354 }
355
356
357 /*
358 * Check the existent ADB devices
359 */
360 void
361 pm_check_adb_devices(id)
362 int id;
363 {
364 u_short ed = 0x1;
365
366 ed <<= id;
367 pm_existent_ADB_devices |= ed;
368 }
369
370
371 /*
372 * Wait until PM IC is busy
373 */
374 int
375 pm_wait_busy(delay)
376 int delay;
377 {
378 while (PM_IS_ON) {
379 #ifdef PM_GRAB_SI
380 #if 0
381 zshard(0); /* grab any serial interrupts */
382 #else
383 (void)intr_dispatch(0x70);
384 #endif
385 #endif
386 if ((--delay) < 0)
387 return 1; /* timeout */
388 }
389 return 0;
390 }
391
392
393 /*
394 * Wait until PM IC is free
395 */
396 int
397 pm_wait_free(delay)
398 int delay;
399 {
400 while (PM_IS_OFF) {
401 #ifdef PM_GRAB_SI
402 #if 0
403 zshard(0); /* grab any serial interrupts */
404 #else
405 (void)intr_dispatch(0x70);
406 #endif
407 #endif
408 if ((--delay) < 0)
409 return 0; /* timeout */
410 }
411 return 1;
412 }
413
414
415
416 /*
417 * Functions for the PB1XX series
418 */
419
420 /*
421 * Receive data from PM for the PB1XX series
422 */
423 int
424 pm_receive_pm1(data)
425 u_char *data;
426 {
427 #if 0
428 int rval = 0xffffcd34;
429
430 via_reg(VIA2, vDirA) = 0x00;
431
432 switch (1) {
433 default:
434 if (pm_wait_busy(0x40) != 0)
435 break; /* timeout */
436
437 PM_SET_STATE_ACKOFF();
438 *data = via_reg(VIA2, 0x200);
439
440 rval = 0xffffcd33;
441 if (pm_wait_free(0x40) == 0)
442 break; /* timeout */
443
444 rval = 0x00;
445 break;
446 }
447
448 PM_SET_STATE_ACKON();
449 via_reg(VIA2, vDirA) = 0x00;
450
451 return rval;
452 #else
453 panic("pm_receive_pm1");
454 #endif
455 }
456
457
458
459 /*
460 * Send data to PM for the PB1XX series
461 */
462 int
463 pm_send_pm1(data, delay)
464 u_char data;
465 int delay;
466 {
467 #if 0
468 int rval;
469
470 via_reg(VIA2, vDirA) = 0xff;
471 via_reg(VIA2, 0x200) = data;
472
473 PM_SET_STATE_ACKOFF();
474 if (pm_wait_busy(0x400) != 0) {
475 PM_SET_STATE_ACKON();
476 via_reg(VIA2, vDirA) = 0x00;
477
478 return 0xffffcd36;
479 }
480
481 rval = 0x0;
482 PM_SET_STATE_ACKON();
483 if (pm_wait_free(0x40) == 0)
484 rval = 0xffffcd35;
485
486 PM_SET_STATE_ACKON();
487 via_reg(VIA2, vDirA) = 0x00;
488
489 return rval;
490 #else
491 panic("pm_send_pm1");
492 #endif
493 }
494
495
496 /*
497 * My PMgrOp routine for the PB1XX series
498 */
499 int
500 pm_pmgrop_pm1(pmdata)
501 PMData *pmdata;
502 {
503 #if 0
504 int i;
505 int s = 0x81815963;
506 u_char via1_vIER, via1_vDirA;
507 int rval = 0;
508 int num_pm_data = 0;
509 u_char pm_cmd;
510 u_char pm_data;
511 u_char *pm_buf;
512
513 /* disable all inetrrupts but PM */
514 via1_vIER = via_reg(VIA1, vIER);
515 PM_VIA_INTR_DISABLE();
516
517 via1_vDirA = via_reg(VIA1, vDirA);
518
519 switch (pmdata->command) {
520 default:
521 for (i = 0; i < 7; i++) {
522 via_reg(VIA2, vDirA) = 0x00;
523
524 /* wait until PM is free */
525 if (pm_wait_free(ADBDelay) == 0) { /* timeout */
526 via_reg(VIA2, vDirA) = 0x00;
527 /* restore formar value */
528 via_reg(VIA1, vDirA) = via1_vDirA;
529 via_reg(VIA1, vIER) = via1_vIER;
530 return 0xffffcd38;
531 }
532
533 switch (mac68k_machine.machineid) {
534 case MACH_MACPB160:
535 case MACH_MACPB165:
536 case MACH_MACPB165C:
537 case MACH_MACPB180:
538 case MACH_MACPB180C:
539 {
540 int delay = ADBDelay * 16;
541
542 via_reg(VIA2, vDirA) = 0x00;
543 while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0))
544 delay--;
545
546 if (delay < 0) { /* timeout */
547 via_reg(VIA2, vDirA) = 0x00;
548 /* restore formar value */
549 via_reg(VIA1, vIER) = via1_vIER;
550 return 0xffffcd38;
551 }
552 }
553 } /* end switch */
554
555 s = splhigh();
556
557 via1_vDirA = via_reg(VIA1, vDirA);
558 via_reg(VIA1, vDirA) &= 0x7f;
559
560 pm_cmd = (u_char)(pmdata->command & 0xff);
561 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0)
562 break; /* send command succeeded */
563
564 via_reg(VIA1, vDirA) = via1_vDirA;
565 splx(s);
566 } /* end for */
567
568 /* failed to send a command */
569 if (i == 7) {
570 via_reg(VIA2, vDirA) = 0x00;
571 /* restore formar value */
572 via_reg(VIA1, vDirA) = via1_vDirA;
573 via_reg(VIA1, vIER) = via1_vIER;
574 if (s != 0x81815963)
575 splx(s);
576 return 0xffffcd38;
577 }
578
579 /* send # of PM data */
580 num_pm_data = pmdata->num_data;
581 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0)
582 break; /* timeout */
583
584 /* send PM data */
585 pm_buf = (u_char *)pmdata->s_buf;
586 for (i = 0; i < num_pm_data; i++)
587 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0)
588 break; /* timeout */
589 if ((i != num_pm_data) && (num_pm_data != 0))
590 break; /* timeout */
591
592 /* Will PM IC return data? */
593 if ((pm_cmd & 0x08) == 0) {
594 rval = 0;
595 break; /* no returned data */
596 }
597
598 rval = 0xffffcd37;
599 if (pm_wait_busy(ADBDelay) != 0)
600 break; /* timeout */
601
602 /* receive PM command */
603 if ((rval = pm_receive_pm1(&pm_data)) != 0)
604 break;
605
606 pmdata->command = pm_data;
607
608 /* receive number of PM data */
609 if ((rval = pm_receive_pm1(&pm_data)) != 0)
610 break; /* timeout */
611 num_pm_data = pm_data;
612 pmdata->num_data = num_pm_data;
613
614 /* receive PM data */
615 pm_buf = (u_char *)pmdata->r_buf;
616 for (i = 0; i < num_pm_data; i++) {
617 if ((rval = pm_receive_pm1(&pm_data)) != 0)
618 break; /* timeout */
619 pm_buf[i] = pm_data;
620 }
621
622 rval = 0;
623 }
624
625 via_reg(VIA2, vDirA) = 0x00;
626
627 /* restore formar value */
628 via_reg(VIA1, vDirA) = via1_vDirA;
629 via_reg(VIA1, vIER) = via1_vIER;
630 if (s != 0x81815963)
631 splx(s);
632
633 return rval;
634 #else
635 panic("pm_pmgrop_pm1");
636 #endif
637 }
638
639
640 /*
641 * My PM interrupt routine for PB1XX series
642 */
643 void
644 pm_intr_pm1()
645 {
646 #if 0
647 int s;
648 int rval;
649 PMData pmdata;
650
651 s = splhigh();
652
653 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
654
655 /* ask PM what happend */
656 pmdata.command = PMU_INT_ACK;
657 pmdata.num_data = 0;
658 pmdata.data[0] = pmdata.data[1] = 0;
659 pmdata.s_buf = &pmdata.data[2];
660 pmdata.r_buf = &pmdata.data[2];
661 rval = pm_pmgrop_pm1(&pmdata);
662 if (rval != 0) {
663 #ifdef ADB_DEBUG
664 if (adb_debug)
665 printf("pm: PM is not ready. error code=%08x\n", rval);
666 #endif
667 splx(s);
668 }
669
670 if ((pmdata.data[2] & 0x10) == 0x10) {
671 if ((pmdata.data[2] & 0x0f) == 0) {
672 /* ADB data that were requested by TALK command */
673 pm_adb_get_TALK_result(&pmdata);
674 } else if ((pmdata.data[2] & 0x08) == 0x8) {
675 /* PM is requesting to poll */
676 pm_adb_poll_next_device_pm1(&pmdata);
677 } else if ((pmdata.data[2] & 0x04) == 0x4) {
678 /* ADB device event */
679 pm_adb_get_ADB_data(&pmdata);
680 }
681 } else {
682 #ifdef ADB_DEBUG
683 if (adb_debug)
684 pm_printerr("driver does not support this event.",
685 rval, pmdata.num_data, pmdata.data);
686 #endif
687 }
688
689 splx(s);
690 #else
691 panic("pm_intr_pm1");
692 #endif
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 void
917 pm_intr_pm2()
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;
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
996
997 /*
998 * My PMgrOp routine
999 */
1000 int
1001 pmgrop(pmdata)
1002 PMData *pmdata;
1003 {
1004 switch (pmHardware) {
1005 case PM_HW_PB1XX:
1006 return (pm_pmgrop_pm1(pmdata));
1007 break;
1008 case PM_HW_PB5XX:
1009 return (pm_pmgrop_pm2(pmdata));
1010 break;
1011 default:
1012 /* return (pmgrop_mrg(pmdata)); */
1013 return 1;
1014 }
1015 }
1016
1017
1018 /*
1019 * My PM interrupt routine
1020 */
1021 void
1022 pm_intr()
1023 {
1024 switch (pmHardware) {
1025 case PM_HW_PB1XX:
1026 pm_intr_pm1();
1027 break;
1028 case PM_HW_PB5XX:
1029 pm_intr_pm2();
1030 break;
1031 default:
1032 break;
1033 }
1034 }
1035
1036
1037
1038 /*
1039 * Synchronous ADBOp routine for the Power Manager
1040 */
1041 int
1042 pm_adb_op(buffer, compRout, data, command)
1043 u_char *buffer;
1044 void *compRout;
1045 void *data;
1046 int command;
1047 {
1048 int i;
1049 int s;
1050 int rval;
1051 int timo;
1052 PMData pmdata;
1053 struct adbCommand packet;
1054
1055 if (adbWaiting == 1)
1056 return 1;
1057
1058 s = splhigh();
1059 write_via_reg(VIA1, vIER, 0x10);
1060
1061 adbBuffer = buffer;
1062 adbCompRout = compRout;
1063 adbCompData = data;
1064
1065 pmdata.command = PMU_ADB_CMD;
1066 pmdata.s_buf = pmdata.data;
1067 pmdata.r_buf = pmdata.data;
1068
1069 /* if the command is LISTEN, add number of ADB data to number of PM data */
1070 if ((command & 0xc) == 0x8) {
1071 if (buffer != (u_char *)0)
1072 pmdata.num_data = buffer[0] + 3;
1073 } else {
1074 pmdata.num_data = 3;
1075 }
1076
1077 pmdata.data[0] = (u_char)(command & 0xff);
1078 pmdata.data[1] = 0;
1079 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */
1080 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
1081 pmdata.data[2] = buffer[0]; /* number of data */
1082 for (i = 0; i < buffer[0]; i++)
1083 pmdata.data[3 + i] = buffer[1 + i];
1084 } else
1085 pmdata.data[2] = 0;
1086 } else
1087 pmdata.data[2] = 0;
1088
1089 if ((command & 0xc) != 0xc) { /* if the command is not TALK */
1090 /* set up stuff for adb_pass_up */
1091 packet.data[0] = 1 + pmdata.data[2];
1092 packet.data[1] = command;
1093 for (i = 0; i < pmdata.data[2]; i++)
1094 packet.data[i+2] = pmdata.data[i+3];
1095 packet.saveBuf = adbBuffer;
1096 packet.compRout = adbCompRout;
1097 packet.compData = adbCompData;
1098 packet.cmd = command;
1099 packet.unsol = 0;
1100 packet.ack_only = 1;
1101 adb_polling = 1;
1102 adb_pass_up(&packet);
1103 adb_polling = 0;
1104 }
1105
1106 rval = pmgrop(&pmdata);
1107 if (rval != 0) {
1108 splx(s);
1109 return 1;
1110 }
1111
1112 delay(10000);
1113
1114 adbWaiting = 1;
1115 adbWaitingCmd = command;
1116
1117 PM_VIA_INTR_ENABLE();
1118
1119 /* wait until the PM interrupt has occurred */
1120 timo = 0x80000;
1121 while (adbWaiting == 1) {
1122 if (read_via_reg(VIA1, vIFR) & 0x14)
1123 pm_intr();
1124 #ifdef PM_GRAB_SI
1125 #if 0
1126 zshard(0); /* grab any serial interrupts */
1127 #else
1128 (void)intr_dispatch(0x70);
1129 #endif
1130 #endif
1131 if ((--timo) < 0) {
1132 /* Try to take an interrupt anyway, just in case.
1133 * This has been observed to happen on my ibook
1134 * when i press a key after boot and before adb
1135 * is attached; For example, when booting with -d.
1136 */
1137 pm_intr();
1138 if (adbWaiting) {
1139 printf("pm_adb_op: timeout. command = 0x%x\n",command);
1140 splx(s);
1141 return 1;
1142 }
1143 #ifdef ADB_DEBUG
1144 else {
1145 printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
1146 }
1147 #endif
1148 }
1149 }
1150
1151 /* this command enables the interrupt by operating ADB devices */
1152 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */
1153 pmdata.command = PMU_ADB_CMD;
1154 pmdata.num_data = 4;
1155 pmdata.s_buf = pmdata.data;
1156 pmdata.r_buf = pmdata.data;
1157 pmdata.data[0] = 0x00;
1158 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */
1159 pmdata.data[2] = 0x00;
1160 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */
1161 } else { /* PB 1XX series */
1162 pmdata.command = PMU_ADB_CMD;
1163 pmdata.num_data = 3;
1164 pmdata.s_buf = pmdata.data;
1165 pmdata.r_buf = pmdata.data;
1166 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc;
1167 pmdata.data[1] = 0x04;
1168 pmdata.data[2] = 0x00;
1169 }
1170 rval = pmgrop(&pmdata);
1171
1172 splx(s);
1173 return rval;
1174 }
1175
1176
1177 void
1178 pm_adb_get_TALK_result(pmdata)
1179 PMData *pmdata;
1180 {
1181 int i;
1182 struct adbCommand packet;
1183
1184 /* set up data for adb_pass_up */
1185 packet.data[0] = pmdata->num_data-1;
1186 packet.data[1] = pmdata->data[3];
1187 for (i = 0; i <packet.data[0]-1; i++)
1188 packet.data[i+2] = pmdata->data[i+4];
1189
1190 packet.saveBuf = adbBuffer;
1191 packet.compRout = adbCompRout;
1192 packet.compData = adbCompData;
1193 packet.unsol = 0;
1194 packet.ack_only = 0;
1195 adb_polling = 1;
1196 adb_pass_up(&packet);
1197 adb_polling = 0;
1198
1199 adbWaiting = 0;
1200 adbBuffer = (long)0;
1201 adbCompRout = (long)0;
1202 adbCompData = (long)0;
1203 }
1204
1205
1206 void
1207 pm_adb_get_ADB_data(pmdata)
1208 PMData *pmdata;
1209 {
1210 int i;
1211 struct adbCommand packet;
1212
1213 if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
1214 pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
1215 ((pmdata->data[2] & ~1) == 0xf4)) {
1216 if (pmdata->data[2] == 0xf4) {
1217 pm_eject_pcmcia(0);
1218 } else {
1219 pm_eject_pcmcia(1);
1220 }
1221 return;
1222 }
1223 /* set up data for adb_pass_up */
1224 packet.data[0] = pmdata->num_data-1; /* number of raw data */
1225 packet.data[1] = pmdata->data[3]; /* ADB command */
1226 for (i = 0; i <packet.data[0]-1; i++)
1227 packet.data[i+2] = pmdata->data[i+4];
1228 packet.unsol = 1;
1229 packet.ack_only = 0;
1230 adb_pass_up(&packet);
1231 }
1232
1233
1234 void
1235 pm_adb_poll_next_device_pm1(pmdata)
1236 PMData *pmdata;
1237 {
1238 int i;
1239 int ndid;
1240 u_short bendid = 0x1;
1241 int rval;
1242 PMData tmp_pmdata;
1243
1244 /* find another existent ADB device to poll */
1245 for (i = 1; i < 16; i++) {
1246 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf;
1247 bendid <<= ndid;
1248 if ((pm_existent_ADB_devices & bendid) != 0)
1249 break;
1250 }
1251
1252 /* poll the other device */
1253 tmp_pmdata.command = PMU_ADB_CMD;
1254 tmp_pmdata.num_data = 3;
1255 tmp_pmdata.s_buf = tmp_pmdata.data;
1256 tmp_pmdata.r_buf = tmp_pmdata.data;
1257 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc;
1258 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */
1259 tmp_pmdata.data[2] = 0x00;
1260 rval = pmgrop(&tmp_pmdata);
1261 }
1262
1263 void
1264 pm_adb_restart()
1265 {
1266 PMData p;
1267
1268 p.command = PMU_RESET_CPU;
1269 p.num_data = 0;
1270 p.s_buf = p.data;
1271 p.r_buf = p.data;
1272 pmgrop(&p);
1273 }
1274
1275 void
1276 pm_adb_poweroff()
1277 {
1278 PMData p;
1279
1280 p.command = PMU_POWER_OFF;
1281 p.num_data = 4;
1282 p.s_buf = p.data;
1283 p.r_buf = p.data;
1284 strcpy(p.data, "MATT");
1285 pmgrop(&p);
1286 }
1287
1288 void
1289 pm_read_date_time(time)
1290 u_long *time;
1291 {
1292 PMData p;
1293
1294 p.command = PMU_READ_RTC;
1295 p.num_data = 0;
1296 p.s_buf = p.data;
1297 p.r_buf = p.data;
1298 pmgrop(&p);
1299
1300 memcpy(time, p.data, 4);
1301 }
1302
1303 void
1304 pm_set_date_time(time)
1305 u_long time;
1306 {
1307 PMData p;
1308
1309 p.command = PMU_SET_RTC;
1310 p.num_data = 4;
1311 p.s_buf = p.r_buf = p.data;
1312 memcpy(p.data, &time, 4);
1313 pmgrop(&p);
1314 }
1315
1316 int
1317 pm_read_brightness()
1318 {
1319 PMData p;
1320
1321 p.command = PMU_READ_BRIGHTNESS;
1322 p.num_data = 1; /* XXX why 1? */
1323 p.s_buf = p.r_buf = p.data;
1324 p.data[0] = 0;
1325 pmgrop(&p);
1326
1327 return p.data[0];
1328 }
1329
1330 void
1331 pm_set_brightness(val)
1332 int val;
1333 {
1334 PMData p;
1335
1336 val = 0x7f - val / 2;
1337 if (val < 0x08)
1338 val = 0x08;
1339 if (val > 0x78)
1340 val = 0x78;
1341
1342 p.command = PMU_SET_BRIGHTNESS;
1343 p.num_data = 1;
1344 p.s_buf = p.r_buf = p.data;
1345 p.data[0] = val;
1346 pmgrop(&p);
1347 }
1348
1349 void
1350 pm_init_brightness()
1351 {
1352 int val;
1353
1354 val = pm_read_brightness();
1355 pm_set_brightness(val);
1356 }
1357
1358 void
1359 pm_eject_pcmcia(slot)
1360 int slot;
1361 {
1362 PMData p;
1363
1364 if (slot != 0 && slot != 1)
1365 return;
1366
1367 p.command = PMU_EJECT_PCMCIA;
1368 p.num_data = 1;
1369 p.s_buf = p.r_buf = p.data;
1370 p.data[0] = 5 + slot; /* XXX */
1371 pmgrop(&p);
1372 }
1373
1374 /*
1375 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
1376 * for a clear description of the PMU results.
1377 */
1378 static int
1379 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
1380 {
1381 PMData p;
1382
1383 p.command = PMU_SMART_BATTERY_STATE;
1384 p.num_data = 1;
1385 p.s_buf = p.r_buf = p.data;
1386 p.data[0] = battery + 1;
1387 pmgrop(&p);
1388
1389 info->flags = p.data[1];
1390
1391 info->secs_remaining = 0;
1392 switch (p.data[0]) {
1393 case 3:
1394 case 4:
1395 info->cur_charge = p.data[2];
1396 info->max_charge = p.data[3];
1397 info->draw = *((signed char *)&p.data[4]);
1398 info->voltage = p.data[5];
1399 break;
1400 case 5:
1401 info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
1402 info->max_charge = ((p.data[4] << 8) | (p.data[5]));
1403 info->draw = *((signed short *)&p.data[6]);
1404 info->voltage = ((p.data[8] << 8) | (p.data[7]));
1405 break;
1406 default:
1407 /* XXX - Error condition */
1408 info->cur_charge = 0;
1409 info->max_charge = 0;
1410 info->draw = 0;
1411 info->voltage = 0;
1412 break;
1413 }
1414 if (info->draw) {
1415 if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
1416 info->secs_remaining =
1417 ((info->max_charge - info->cur_charge) * 3600)
1418 / info->draw;
1419 } else {
1420 info->secs_remaining =
1421 (info->cur_charge * 3600) / -info->draw;
1422 }
1423 }
1424
1425 return 1;
1426 }
1427
1428 static int
1429 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
1430 {
1431 PMData p;
1432 long pcharge=0, charge, vb, vmax, lmax;
1433 long vmax_charging, vmax_charged, amperage, voltage;
1434
1435 p.command = PMU_BATTERY_STATE;
1436 p.num_data = 0;
1437 p.s_buf = p.r_buf = p.data;
1438 pmgrop(&p);
1439
1440 info->flags = p.data[0];
1441
1442 if (info->flags & PMU_PWR_BATT_PRESENT) {
1443 if (ty == BATT_COMET) {
1444 vmax_charging = 213;
1445 vmax_charged = 189;
1446 lmax = 6500;
1447 } else {
1448 /* Experimental values */
1449 vmax_charging = 365;
1450 vmax_charged = 365;
1451 lmax = 6500;
1452 }
1453 vmax = vmax_charged;
1454 vb = (p.data[1] << 8) | p.data[2];
1455 voltage = (vb * 256 + 72665) / 10;
1456 amperage = (unsigned char) p.data[5];
1457 if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1458 if (amperage > 200)
1459 vb += ((amperage - 200) * 15)/100;
1460 } else if (info->flags & PMU_PWR_BATT_CHARGING) {
1461 vb = (vb * 97) / 100;
1462 vmax = vmax_charging;
1463 }
1464 charge = (100 * vb) / vmax;
1465 if (info->flags & PMU_PWR_PCHARGE_RESET) {
1466 pcharge = (p.data[6] << 8) | p.data[7];
1467 if (pcharge > lmax)
1468 pcharge = lmax;
1469 pcharge *= 100;
1470 pcharge = 100 - pcharge / lmax;
1471 if (pcharge < charge)
1472 charge = pcharge;
1473 }
1474 info->cur_charge = charge;
1475 info->max_charge = 100;
1476 info->draw = -amperage;
1477 info->voltage = voltage;
1478 if (amperage > 0)
1479 info->secs_remaining = (charge * 16440) / amperage;
1480 else
1481 info->secs_remaining = 0;
1482 } else {
1483 info->cur_charge = 0;
1484 info->max_charge = 0;
1485 info->draw = 0;
1486 info->voltage = 0;
1487 info->secs_remaining = 0;
1488 }
1489
1490 return 1;
1491 }
1492
1493 int
1494 pm_battery_info(int battery, struct pmu_battery_info *info)
1495 {
1496
1497 if (battery > pmu_nbatt)
1498 return 0;
1499
1500 switch (pmu_batt_type) {
1501 case BATT_COMET:
1502 case BATT_HOOPER:
1503 return pm_battery_info_legacy(battery, info, pmu_batt_type);
1504
1505 case BATT_SMART:
1506 return pm_battery_info_smart(battery, info);
1507 }
1508
1509 return 0;
1510 }
1511
1512 int
1513 pm_read_nvram(addr)
1514 int addr;
1515 {
1516 PMData p;
1517
1518 p.command = PMU_READ_NVRAM;
1519 p.num_data = 2;
1520 p.s_buf = p.r_buf = p.data;
1521 p.data[0] = addr >> 8;
1522 p.data[1] = addr;
1523 pmgrop(&p);
1524
1525 return p.data[0];
1526 }
1527
1528 void
1529 pm_write_nvram(addr, val)
1530 int addr, val;
1531 {
1532 PMData p;
1533
1534 p.command = PMU_WRITE_NVRAM;
1535 p.num_data = 3;
1536 p.s_buf = p.r_buf = p.data;
1537 p.data[0] = addr >> 8;
1538 p.data[1] = addr;
1539 p.data[2] = val;
1540 pmgrop(&p);
1541 }
1542