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