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