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