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