1 /* $NetBSD: pm_direct.c,v 1.39 2024/06/02 13:28:46 andvar Exp $ */
2
3 /*
4 * Copyright (C) 1997 Takashi Hamada
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Takashi Hamada
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */
33
34 /*
35 * TODO : Check bounds on PMData in pmgrop
36 * callers should specify how much room for data is in the buffer
37 * and that should be respected by the pmgrop
38 */
39
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.39 2024/06/02 13:28:46 andvar Exp $");
42
43 #ifdef DEBUG
44 #ifndef ADB_DEBUG
45 #define ADB_DEBUG
46 #endif
47 #endif
48
49 /* #define PM_GRAB_SI 1 */
50
51 #include <sys/param.h>
52 #include <sys/device.h>
53 #include <sys/systm.h>
54
55 #include <machine/adbsys.h>
56 #include <machine/autoconf.h>
57 #include <machine/cpu.h>
58 #include <machine/pio.h>
59
60 #include <dev/ofw/openfirm.h>
61
62 #include <macppc/dev/adbvar.h>
63 #include <macppc/dev/pm_direct.h>
64 #include <macppc/dev/viareg.h>
65
66 extern int adb_polling; /* Are we polling? (Debugger mode) */
67
68 /* hardware dependent values */
69 #define ADBDelay 100 /* XXX */
70
71 /* useful macros */
72 #define PM_SR() read_via_reg(VIA1, vSR)
73 #define PM_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x90)
74 #define PM_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x10)
75 #define PM_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x90)
76
77 #define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x10)
78 #define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x10)
79 #define PM_IS_ON (0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
80 #define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
81
82 /*
83 * Variables for internal use
84 */
85 u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */
86 u_int pm_LCD_brightness = 0x0;
87 u_int pm_LCD_contrast = 0x0;
88 u_int pm_counter = 0; /* clock count */
89
90 static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
91 static int pmu_nbatt;
92 static int strinlist(const char *, char *, int);
93 static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type;
94
95 /* these values shows that number of data returned after 'send' cmd is sent */
96 signed char pm_send_cmd_type[] = {
97 -1, -1, -1, -1, -1, -1, -1, -1,
98 -1, -1, -1, -1, -1, -1, -1, -1,
99 0x01, 0x01, -1, -1, -1, -1, -1, -1,
100 0x00, 0x00, -1, -1, -1, -1, -1, 0x00,
101 -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1,
102 0x00, -1, -1, -1, -1, -1, -1, -1,
103 0x04, 0x14, -1, 0x03, -1, -1, -1, -1,
104 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1,
105 0x01, 0x01, -1, -1, -1, -1, -1, -1,
106 0x00, 0x00, -1, -1, 0x01, -1, -1, -1,
107 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01,
108 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1,
109 0x02, -1, -1, -1, -1, -1, -1, -1,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1,
111 0x01, 0x01, 0x01, -1, -1, -1, -1, -1,
112 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04,
113 0x04, -1, 0x00, -1, -1, -1, -1, -1,
114 0x00, -1, -1, -1, -1, -1, -1, -1,
115 0x01, 0x02, -1, -1, -1, -1, -1, -1,
116 0x00, 0x00, -1, -1, -1, -1, -1, -1,
117 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1,
118 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1,
119 -1, -1, -1, -1, -1, -1, -1, -1,
120 -1, -1, -1, -1, -1, -1, -1, -1,
121 -1, -1, -1, -1, -1, -1, -1, -1,
122 -1, -1, -1, -1, -1, -1, -1, -1,
123 0x00, -1, -1, -1, -1, -1, -1, -1,
124 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1,
125 -1, 0x04, 0x00, -1, -1, -1, -1, -1,
126 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00,
127 -1, -1, -1, -1, -1, -1, -1, -1,
128 -1, -1, -1, -1, -1, -1, -1, -1
129 };
130
131 /* these values shows that number of data returned after 'receive' cmd is sent */
132 signed char pm_receive_cmd_type[] = {
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 -1, -1, -1, -1, -1, -1, -1, -1,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x02, 0x02, -1, -1, -1, -1, -1, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 -1, -1, -1, -1, -1, -1, -1, -1,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0x05, 0x15, -1, 0x02, -1, -1, -1, -1,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x02, 0x02, -1, -1, -1, -1, -1, -1,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 -1, -1, -1, -1, -1, -1, 0x01, 0x01,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x06, -1, -1, -1, -1, -1, -1, -1,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x02, 0x02, -1, -1, -1, -1, -1, -1,
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 -1, -1, -1, -1, -1, -1, -1, -1,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 -1, -1, -1, -1, -1, -1, -1, -1,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x02, 0x02, -1, -1, 0x02, -1, -1, -1,
161 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
162 -1, -1, 0x02, -1, -1, -1, -1, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 -1, -1, -1, -1, -1, -1, -1, -1,
165 };
166
167
168 /*
169 * Define the private functions
170 */
171
172 /* for debugging */
173 #ifdef ADB_DEBUG
174 void pm_printerr(const char *, int, int, const char *);
175 #endif
176
177 int pm_wait_busy(int);
178 int pm_wait_free(int);
179
180 static int pm_receive(u_char *);
181 static int pm_send(u_char);
182
183 /* these functions are called from adb_direct.c */
184 void pm_setup_adb(void);
185 void pm_check_adb_devices(int);
186 int pm_adb_op(u_char *, adbComp *, volatile int *, int);
187
188 /* these functions also use the variables of adb_direct.c */
189 void pm_adb_get_TALK_result(PMData *);
190 void pm_adb_get_ADB_data(PMData *);
191
192
193 /*
194 * These variables are in adb_direct.c.
195 */
196 extern u_char *adbBuffer; /* pointer to user data area */
197 extern adbComp *adbCompRout; /* pointer to the completion routine */
198 extern volatile int *adbCompData; /* pointer to the completion routine data */
199 extern int adbWaiting; /* waiting for return data from the device */
200 extern int adbWaitingCmd; /* ADB command we are waiting for */
201 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */
202
203 #define ADB_MAX_MSG_LENGTH 16
204 #define ADB_MAX_HDR_LENGTH 8
205 struct adbCommand {
206 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */
207 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */
208 u_char *saveBuf; /* where to save result */
209 adbComp *compRout; /* completion routine pointer */
210 volatile int *compData; /* completion routine data pointer */
211 u_int cmd; /* the original command for this data */
212 u_int unsol; /* 1 if packet was unsolicited */
213 u_int ack_only; /* 1 for no special processing */
214 };
215 extern void adb_pass_up(struct adbCommand *);
216
217 #if 0
218 /*
219 * Define the external functions
220 */
221 extern int zshard(int); /* from zs.c */
222 #endif
223
224 #ifdef ADB_DEBUG
225 /*
226 * This function dumps contents of the PMData
227 */
228 void
pm_printerr(const char * ttl,int rval,int num,const char * data)229 pm_printerr(const char *ttl, int rval, int num, const char *data)
230 {
231 int i;
232
233 printf("pm: %s:%04x %02x ", ttl, rval, num);
234 for (i = 0; i < num; i++)
235 printf("%02x ", data[i]);
236 printf("\n");
237 }
238 #endif
239
240
241
242 /*
243 * Check the hardware type of the Power Manager
244 */
245 void
pm_setup_adb(void)246 pm_setup_adb(void)
247 {
248 }
249
250 /*
251 * Search for targ in list. list is an area of listlen bytes
252 * containing null-terminated strings.
253 */
254 static int
strinlist(const char * targ,char * list,int listlen)255 strinlist(const char *targ, char *list, int listlen)
256 {
257 char *str;
258 int sl;
259 int targlen;
260
261 str = list;
262 targlen = strlen(targ);
263 while (listlen > 0) {
264 sl = strlen(str);
265 if (sl == targlen && (strncmp(targ, str, sl) == 0))
266 return 1;
267 str += sl+1;
268 listlen -= sl+1;
269 }
270 return 0;
271 }
272
273 /*
274 * Check the hardware type of the Power Manager
275 */
276 void
pm_init(void)277 pm_init(void)
278 {
279 uint32_t regs[10];
280 PMData pmdata;
281 char compat[128];
282 int clen, node, pm_imask;
283
284 node = OF_peer(0);
285 if (node == -1) {
286 printf("pmu: Failed to get root");
287 return;
288 }
289 clen = OF_getprop(node, "compatible", compat, sizeof(compat));
290 if (clen <= 0) {
291 printf("pmu: failed to read root compatible data %d\n", clen);
292 return;
293 }
294
295 pm_imask =
296 PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
297
298 if (strinlist("AAPL,3500", compat, clen) ||
299 strinlist("AAPL,3400/2400", compat, clen)) {
300 /* How to distinguish BATT_COMET? */
301 pmu_nbatt = 1;
302 pmu_batt_type = BATT_HOOPER;
303 pmu_type = PMU_OHARE;
304 } else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
305 strinlist("PowerBook1,1", compat, clen)) {
306 pmu_nbatt = 2;
307 pmu_batt_type = BATT_SMART;
308 pmu_type = PMU_G3;
309 } else {
310 pmu_nbatt = 1;
311 pmu_batt_type = BATT_SMART;
312 pmu_type = PMU_KEYLARGO;
313 node = of_getnode_byname(0, "power-mgt");
314 if (node == -1) {
315 printf("pmu: can't find power-mgt\n");
316 return;
317 }
318 clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
319 if (clen < 24) {
320 printf("pmu: failed to read prim-info\n");
321 return;
322 }
323 pmu_nbatt = regs[6] >> 16;
324 }
325
326 pmdata.command = PMU_SET_IMASK;
327 pmdata.num_data = 1;
328 pmdata.s_buf = pmdata.data;
329 pmdata.r_buf = pmdata.data;
330 pmdata.data[0] = pm_imask;
331 pmgrop(&pmdata);
332 }
333
334
335 /*
336 * Check the existent ADB devices
337 */
338 void
pm_check_adb_devices(int id)339 pm_check_adb_devices(int id)
340 {
341 u_short ed = 0x1;
342
343 ed <<= id;
344 pm_existent_ADB_devices |= ed;
345 }
346
347
348 /*
349 * Wait until PM IC is busy
350 */
351 int
pm_wait_busy(int delaycycles)352 pm_wait_busy(int delaycycles)
353 {
354 while (PM_IS_ON) {
355 #ifdef PM_GRAB_SI
356 #if 0
357 zshard(0); /* grab any serial interrupts */
358 #else
359 (void)intr_dispatch(0x70);
360 #endif
361 #endif
362 if ((--delaycycles) < 0)
363 return 1; /* timeout */
364 }
365 return 0;
366 }
367
368
369 /*
370 * Wait until PM IC is free
371 */
372 int
pm_wait_free(int delaycycles)373 pm_wait_free(int delaycycles)
374 {
375 while (PM_IS_OFF) {
376 #ifdef PM_GRAB_SI
377 #if 0
378 zshard(0); /* grab any serial interrupts */
379 #else
380 (void)intr_dispatch(0x70);
381 #endif
382 #endif
383 if ((--delaycycles) < 0)
384 return 0; /* timeout */
385 }
386 return 1;
387 }
388
389
390
391 /*
392 * Receive data from PMU
393 */
394 static int
pm_receive(u_char * data)395 pm_receive(u_char *data)
396 {
397 int i;
398 int rval;
399
400 rval = 0xffffcd34;
401
402 switch (1) {
403 default:
404 /* set VIA SR to input mode */
405 via_reg_or(VIA1, vACR, 0x0c);
406 via_reg_and(VIA1, vACR, ~0x10);
407 i = PM_SR();
408
409 PM_SET_STATE_ACKOFF();
410 if (pm_wait_busy((int)ADBDelay*32) != 0)
411 break; /* timeout */
412
413 PM_SET_STATE_ACKON();
414 rval = 0xffffcd33;
415 if (pm_wait_free((int)ADBDelay*32) == 0)
416 break; /* timeout */
417
418 *data = PM_SR();
419 rval = 0;
420
421 break;
422 }
423
424 PM_SET_STATE_ACKON();
425 via_reg_or(VIA1, vACR, 0x1c);
426
427 return rval;
428 }
429
430
431
432 /*
433 * Send data to PMU
434 */
435 static int
pm_send(u_char data)436 pm_send(u_char data)
437 {
438 int rval;
439
440 via_reg_or(VIA1, vACR, 0x1c);
441 write_via_reg(VIA1, vSR, data); /* PM_SR() = data; */
442
443 PM_SET_STATE_ACKOFF();
444 rval = 0xffffcd36;
445 if (pm_wait_busy((int)ADBDelay*32) != 0) {
446 PM_SET_STATE_ACKON();
447
448 via_reg_or(VIA1, vACR, 0x1c);
449
450 return rval;
451 }
452
453 PM_SET_STATE_ACKON();
454 rval = 0xffffcd35;
455 if (pm_wait_free((int)ADBDelay*32) != 0)
456 rval = 0;
457
458 PM_SET_STATE_ACKON();
459 via_reg_or(VIA1, vACR, 0x1c);
460
461 return rval;
462 }
463
464
465
466 /*
467 * The PMgrOp routine
468 */
469 int
pmgrop(PMData * pmdata)470 pmgrop(PMData *pmdata)
471 {
472 int i;
473 int s;
474 u_char via1_vIER;
475 int rval = 0;
476 int num_pm_data = 0;
477 u_char pm_cmd;
478 short pm_num_rx_data;
479 u_char pm_data;
480 u_char *pm_buf;
481
482 s = splhigh();
483
484 /* disable all interrupts but PM */
485 via1_vIER = 0x10;
486 via1_vIER &= read_via_reg(VIA1, vIER);
487 write_via_reg(VIA1, vIER, via1_vIER);
488 if (via1_vIER != 0x0)
489 via1_vIER |= 0x80;
490
491 switch (pmdata->command) {
492 default:
493 /* wait until PM is free */
494 pm_cmd = (u_char)(pmdata->command & 0xff);
495 rval = 0xcd38;
496 if (pm_wait_free(ADBDelay * 4) == 0)
497 break; /* timeout */
498
499 /* send PM command */
500 if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
501 break; /* timeout */
502
503 /* send number of PM data */
504 num_pm_data = pmdata->num_data;
505 if (pm_send_cmd_type[pm_cmd] < 0) {
506 if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
507 break; /* timeout */
508 pmdata->command = 0;
509 }
510 /* send PM data */
511 pm_buf = (u_char *)pmdata->s_buf;
512 for (i = 0 ; i < num_pm_data; i++)
513 if ((rval = pm_send(pm_buf[i])) != 0)
514 break; /* timeout */
515 if (i != num_pm_data)
516 break; /* timeout */
517
518
519 /* check if PM will send me data */
520 pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
521 pmdata->num_data = pm_num_rx_data;
522 if (pm_num_rx_data == 0) {
523 rval = 0;
524 break; /* no return data */
525 }
526
527 /* receive PM command */
528 pm_data = pmdata->command;
529 pm_num_rx_data--;
530 if (pm_num_rx_data == 0)
531 if ((rval = pm_receive(&pm_data)) != 0) {
532 rval = 0xffffcd37;
533 break;
534 }
535 pmdata->command = pm_data;
536
537 /* receive number of PM data */
538 if (pm_num_rx_data < 0) {
539 if ((rval = pm_receive(&pm_data)) != 0)
540 break; /* timeout */
541 num_pm_data = pm_data;
542 } else
543 num_pm_data = pm_num_rx_data;
544 pmdata->num_data = num_pm_data;
545
546 /* receive PM data */
547 pm_buf = (u_char *)pmdata->r_buf;
548 for (i = 0; i < num_pm_data; i++) {
549 if ((rval = pm_receive(&pm_data)) != 0)
550 break; /* timeout */
551 pm_buf[i] = pm_data;
552 }
553
554 rval = 0;
555 }
556
557 /* restore former value */
558 write_via_reg(VIA1, vIER, via1_vIER);
559 splx(s);
560
561 return rval;
562 }
563
564
565 /*
566 * My PMU interrupt routine
567 */
568 int
pm_intr(void * arg)569 pm_intr(void *arg)
570 {
571 int s;
572 int rval;
573 PMData pmdata;
574
575 s = splhigh();
576
577 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */
578 /* ask PM what happened */
579 pmdata.command = PMU_INT_ACK;
580 pmdata.num_data = 0;
581 pmdata.s_buf = &pmdata.data[2];
582 pmdata.r_buf = &pmdata.data[2];
583 rval = pmgrop(&pmdata);
584 if (rval != 0) {
585 #ifdef ADB_DEBUG
586 if (adb_debug)
587 printf("pm: PM is not ready. error code: %08x\n", rval);
588 #endif
589 splx(s);
590 return 0;
591 }
592
593 switch ((u_int)(pmdata.data[2] & 0xff)) {
594 case 0x00: /* no event pending? */
595 break;
596 case 0x80: /* 1 sec interrupt? */
597 pm_counter++;
598 break;
599 case 0x08: /* Brightness/Contrast button on LCD panel */
600 /* get brightness and contrast of the LCD */
601 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
602 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
603
604 /* this is experimental code */
605 pmdata.command = PMU_SET_BRIGHTNESS;
606 pmdata.num_data = 1;
607 pmdata.s_buf = pmdata.data;
608 pmdata.r_buf = pmdata.data;
609 pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
610 if (pm_LCD_brightness < 0x08)
611 pm_LCD_brightness = 0x08;
612 if (pm_LCD_brightness > 0x78)
613 pm_LCD_brightness = 0x78;
614 pmdata.data[0] = pm_LCD_brightness;
615 rval = pmgrop(&pmdata);
616 break;
617
618 case 0x10: /* ADB data requested by TALK command */
619 case 0x14:
620 pm_adb_get_TALK_result(&pmdata);
621 break;
622 case 0x16: /* ADB device event */
623 case 0x18:
624 case 0x1e:
625 pm_adb_get_ADB_data(&pmdata);
626 break;
627 default:
628 #ifdef ADB_DEBUG
629 if (adb_debug)
630 pm_printerr("driver does not support this event.",
631 pmdata.data[2], pmdata.num_data,
632 pmdata.data);
633 #endif
634 break;
635 }
636
637 splx(s);
638
639 return 1;
640 }
641
642
643 /*
644 * Synchronous ADBOp routine for the Power Manager
645 */
646 int
pm_adb_op(u_char * buffer,adbComp * compRout,volatile int * data,int command)647 pm_adb_op(u_char *buffer, adbComp *compRout, volatile int *data, int command)
648 {
649 int i;
650 int s;
651 int rval;
652 int timo;
653 PMData pmdata;
654 struct adbCommand packet;
655
656 if (adbWaiting == 1)
657 return 1;
658
659 s = splhigh();
660 write_via_reg(VIA1, vIER, 0x10);
661
662 adbBuffer = buffer;
663 adbCompRout = compRout;
664 adbCompData = data;
665
666 pmdata.command = PMU_ADB_CMD;
667 pmdata.s_buf = pmdata.data;
668 pmdata.r_buf = pmdata.data;
669
670 /* if the command is LISTEN, add number of ADB data to number of PM data */
671 if ((command & 0xc) == 0x8) {
672 if (buffer != (u_char *)0)
673 pmdata.num_data = buffer[0] + 3;
674 } else {
675 pmdata.num_data = 3;
676 }
677
678 pmdata.data[0] = (u_char)(command & 0xff);
679 pmdata.data[1] = 0;
680 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */
681 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
682 pmdata.data[2] = buffer[0]; /* number of data */
683 for (i = 0; i < buffer[0]; i++)
684 pmdata.data[3 + i] = buffer[1 + i];
685 } else
686 pmdata.data[2] = 0;
687 } else
688 pmdata.data[2] = 0;
689
690 if ((command & 0xc) != 0xc) { /* if the command is not TALK */
691 /* set up stuff for adb_pass_up */
692 packet.data[0] = 1 + pmdata.data[2];
693 packet.data[1] = command;
694 for (i = 0; i < pmdata.data[2]; i++)
695 packet.data[i+2] = pmdata.data[i+3];
696 packet.saveBuf = adbBuffer;
697 packet.compRout = adbCompRout;
698 packet.compData = adbCompData;
699 packet.cmd = command;
700 packet.unsol = 0;
701 packet.ack_only = 1;
702 adb_polling = 1;
703 adb_pass_up(&packet);
704 adb_polling = 0;
705 }
706
707 rval = pmgrop(&pmdata);
708 if (rval != 0) {
709 splx(s);
710 return 1;
711 }
712
713 delay(10000);
714
715 adbWaiting = 1;
716 adbWaitingCmd = command;
717
718 PM_VIA_INTR_ENABLE();
719
720 /* wait until the PM interrupt has occurred */
721 timo = 0x80000;
722 while (adbWaiting == 1) {
723 if (read_via_reg(VIA1, vIFR) & 0x14)
724 pm_intr(NULL);
725 #ifdef PM_GRAB_SI
726 #if 0
727 zshard(0); /* grab any serial interrupts */
728 #else
729 (void)intr_dispatch(0x70);
730 #endif
731 #endif
732 if ((--timo) < 0) {
733 /* Try to take an interrupt anyway, just in case.
734 * This has been observed to happen on my ibook
735 * when i press a key after boot and before adb
736 * is attached; For example, when booting with -d.
737 */
738 pm_intr(NULL);
739 if (adbWaiting) {
740 printf("pm_adb_op: timeout. command = 0x%x\n",command);
741 splx(s);
742 return 1;
743 }
744 #ifdef ADB_DEBUG
745 else {
746 printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
747 }
748 #endif
749 }
750 }
751
752 /* this command enables the interrupt by operating ADB devices */
753 pmdata.command = PMU_ADB_CMD;
754 pmdata.num_data = 4;
755 pmdata.s_buf = pmdata.data;
756 pmdata.r_buf = pmdata.data;
757 pmdata.data[0] = 0x00;
758 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */
759 pmdata.data[2] = 0x00;
760 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */
761 rval = pmgrop(&pmdata);
762
763 splx(s);
764 return rval;
765 }
766
767
768 void
pm_adb_get_TALK_result(PMData * pmdata)769 pm_adb_get_TALK_result(PMData *pmdata)
770 {
771 int i;
772 struct adbCommand packet;
773
774 /* set up data for adb_pass_up */
775 packet.data[0] = pmdata->num_data-1;
776 packet.data[1] = pmdata->data[3];
777 for (i = 0; i <packet.data[0]-1; i++)
778 packet.data[i+2] = pmdata->data[i+4];
779
780 packet.saveBuf = adbBuffer;
781 packet.compRout = adbCompRout;
782 packet.compData = adbCompData;
783 packet.unsol = 0;
784 packet.ack_only = 0;
785 adb_polling = 1;
786 adb_pass_up(&packet);
787 adb_polling = 0;
788
789 adbWaiting = 0;
790 adbBuffer = (long)0;
791 adbCompRout = (long)0;
792 adbCompData = (long)0;
793 }
794
795
796 void
pm_adb_get_ADB_data(PMData * pmdata)797 pm_adb_get_ADB_data(PMData *pmdata)
798 {
799 int i;
800 struct adbCommand packet;
801
802 if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
803 pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
804 ((pmdata->data[2] & ~1) == 0xf4)) {
805 if (pmdata->data[2] == 0xf4) {
806 pm_eject_pcmcia(0);
807 } else {
808 pm_eject_pcmcia(1);
809 }
810 return;
811 }
812 /* set up data for adb_pass_up */
813 packet.data[0] = pmdata->num_data-1; /* number of raw data */
814 packet.data[1] = pmdata->data[3]; /* ADB command */
815 for (i = 0; i <packet.data[0]-1; i++)
816 packet.data[i+2] = pmdata->data[i+4];
817 packet.unsol = 1;
818 packet.ack_only = 0;
819 adb_pass_up(&packet);
820 }
821
822
823 void
pm_adb_restart(void)824 pm_adb_restart(void)
825 {
826 PMData p;
827
828 p.command = PMU_RESET_CPU;
829 p.num_data = 0;
830 p.s_buf = p.data;
831 p.r_buf = p.data;
832 pmgrop(&p);
833 }
834
835 void
pm_adb_poweroff(void)836 pm_adb_poweroff(void)
837 {
838 PMData p;
839
840 p.command = PMU_POWER_OFF;
841 p.num_data = 4;
842 p.s_buf = p.data;
843 p.r_buf = p.data;
844 strcpy(p.data, "MATT");
845 pmgrop(&p);
846 }
847
848 void
pm_read_date_time(u_long * t)849 pm_read_date_time(u_long *t)
850 {
851 PMData p;
852
853 p.command = PMU_READ_RTC;
854 p.num_data = 0;
855 p.s_buf = p.data;
856 p.r_buf = p.data;
857 pmgrop(&p);
858
859 memcpy(t, p.data, 4);
860 }
861
862 void
pm_set_date_time(u_long t)863 pm_set_date_time(u_long t)
864 {
865 PMData p;
866
867 p.command = PMU_SET_RTC;
868 p.num_data = 4;
869 p.s_buf = p.r_buf = p.data;
870 memcpy(p.data, &t, 4);
871 pmgrop(&p);
872 }
873
874 int
pm_read_brightness(void)875 pm_read_brightness(void)
876 {
877 PMData p;
878
879 p.command = PMU_READ_BRIGHTNESS;
880 p.num_data = 1; /* XXX why 1? */
881 p.s_buf = p.r_buf = p.data;
882 p.data[0] = 0;
883 pmgrop(&p);
884
885 return p.data[0];
886 }
887
888 void
pm_set_brightness(int val)889 pm_set_brightness(int val)
890 {
891 PMData p;
892
893 val = 0x7f - val / 2;
894 if (val < 0x08)
895 val = 0x08;
896 if (val > 0x78)
897 val = 0x78;
898
899 p.command = PMU_SET_BRIGHTNESS;
900 p.num_data = 1;
901 p.s_buf = p.r_buf = p.data;
902 p.data[0] = val;
903 pmgrop(&p);
904 }
905
906 void
pm_init_brightness(void)907 pm_init_brightness(void)
908 {
909 int val;
910
911 val = pm_read_brightness();
912 pm_set_brightness(val);
913 }
914
915 void
pm_eject_pcmcia(int slot)916 pm_eject_pcmcia(int slot)
917 {
918 PMData p;
919
920 if (slot != 0 && slot != 1)
921 return;
922
923 p.command = PMU_EJECT_PCMCIA;
924 p.num_data = 1;
925 p.s_buf = p.r_buf = p.data;
926 p.data[0] = 5 + slot; /* XXX */
927 pmgrop(&p);
928 }
929
930 /*
931 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
932 * for a clear description of the PMU results.
933 */
934 static int
pm_battery_info_smart(int battery,struct pmu_battery_info * info)935 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
936 {
937 PMData p;
938
939 p.command = PMU_SMART_BATTERY_STATE;
940 p.num_data = 1;
941 p.s_buf = p.r_buf = p.data;
942 p.data[0] = battery + 1;
943 pmgrop(&p);
944
945 info->flags = p.data[1];
946
947 info->secs_remaining = 0;
948 switch (p.data[0]) {
949 case 3:
950 case 4:
951 info->cur_charge = p.data[2];
952 info->max_charge = p.data[3];
953 info->draw = *((signed char *)&p.data[4]);
954 info->voltage = p.data[5];
955 break;
956 case 5:
957 info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
958 info->max_charge = ((p.data[4] << 8) | (p.data[5]));
959 info->draw = *((signed short *)&p.data[6]);
960 info->voltage = ((p.data[8] << 8) | (p.data[7]));
961 break;
962 default:
963 /* XXX - Error condition */
964 info->cur_charge = 0;
965 info->max_charge = 0;
966 info->draw = 0;
967 info->voltage = 0;
968 break;
969 }
970 if (info->draw) {
971 if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
972 info->secs_remaining =
973 ((info->max_charge - info->cur_charge) * 3600)
974 / info->draw;
975 } else {
976 info->secs_remaining =
977 (info->cur_charge * 3600) / -info->draw;
978 }
979 }
980
981 return 1;
982 }
983
984 static int
pm_battery_info_legacy(int battery,struct pmu_battery_info * info,int ty)985 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
986 {
987 PMData p;
988 long pcharge=0, charge, vb, vmax, chargemax;
989 long vmax_charging, vmax_charged, amperage, voltage;
990
991 p.command = PMU_BATTERY_STATE;
992 p.num_data = 0;
993 p.s_buf = p.r_buf = p.data;
994 pmgrop(&p);
995
996 info->flags = p.data[0];
997
998 if (info->flags & PMU_PWR_BATT_PRESENT) {
999 if (ty == BATT_COMET) {
1000 vmax_charging = 213;
1001 vmax_charged = 189;
1002 chargemax = 6500;
1003 } else {
1004 /* Experimental values */
1005 vmax_charging = 365;
1006 vmax_charged = 365;
1007 chargemax = 6500;
1008 }
1009 vmax = vmax_charged;
1010 vb = (p.data[1] << 8) | p.data[2];
1011 voltage = (vb * 256 + 72665) / 10;
1012 amperage = (unsigned char) p.data[5];
1013 if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
1014 if (amperage > 200)
1015 vb += ((amperage - 200) * 15)/100;
1016 } else if (info->flags & PMU_PWR_BATT_CHARGING) {
1017 vb = (vb * 97) / 100;
1018 vmax = vmax_charging;
1019 }
1020 charge = (100 * vb) / vmax;
1021 if (info->flags & PMU_PWR_PCHARGE_RESET) {
1022 pcharge = (p.data[6] << 8) | p.data[7];
1023 if (pcharge > chargemax)
1024 pcharge = chargemax;
1025 pcharge *= 100;
1026 pcharge = 100 - pcharge / chargemax;
1027 if (pcharge < charge)
1028 charge = pcharge;
1029 }
1030 info->cur_charge = charge;
1031 info->max_charge = 100;
1032 info->draw = -amperage;
1033 info->voltage = voltage;
1034 if (amperage > 0)
1035 info->secs_remaining = (charge * 16440) / amperage;
1036 else
1037 info->secs_remaining = 0;
1038 } else {
1039 info->cur_charge = 0;
1040 info->max_charge = 0;
1041 info->draw = 0;
1042 info->voltage = 0;
1043 info->secs_remaining = 0;
1044 }
1045
1046 return 1;
1047 }
1048
1049 int
pm_battery_info(int battery,struct pmu_battery_info * info)1050 pm_battery_info(int battery, struct pmu_battery_info *info)
1051 {
1052
1053 if (battery > pmu_nbatt)
1054 return 0;
1055
1056 switch (pmu_batt_type) {
1057 case BATT_COMET:
1058 case BATT_HOOPER:
1059 return pm_battery_info_legacy(battery, info, pmu_batt_type);
1060
1061 case BATT_SMART:
1062 return pm_battery_info_smart(battery, info);
1063 }
1064
1065 return 0;
1066 }
1067
1068 int
pm_read_nvram(int addr)1069 pm_read_nvram(int addr)
1070 {
1071 PMData p;
1072
1073 p.command = PMU_READ_NVRAM;
1074 p.num_data = 2;
1075 p.s_buf = p.r_buf = p.data;
1076 p.data[0] = addr >> 8;
1077 p.data[1] = addr;
1078 pmgrop(&p);
1079
1080 return p.data[0];
1081 }
1082
1083 void
pm_write_nvram(int addr,int val)1084 pm_write_nvram(int addr, int val)
1085 {
1086 PMData p;
1087
1088 p.command = PMU_WRITE_NVRAM;
1089 p.num_data = 3;
1090 p.s_buf = p.r_buf = p.data;
1091 p.data[0] = addr >> 8;
1092 p.data[1] = addr;
1093 p.data[2] = val;
1094 pmgrop(&p);
1095 }
1096