pm_direct.c revision 1.35
1/* $NetBSD: pm_direct.c,v 1.35 2009/03/14 15:36:09 dsl 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.35 2009/03/14 15:36:09 dsl 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 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(const 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(const char *, int, int, const char *); 175#endif 176 177int pm_wait_busy(int); 178int pm_wait_free(int); 179 180static int pm_receive(u_char *); 181static int pm_send(u_char); 182 183/* these functions are called from adb_direct.c */ 184void pm_setup_adb(void); 185void pm_check_adb_devices(int); 186int pm_adb_op(u_char *, adbComp *, volatile int *, int); 187 188/* these functions also use the variables of adb_direct.c */ 189void pm_adb_get_TALK_result(PMData *); 190void pm_adb_get_ADB_data(PMData *); 191 192 193/* 194 * These variables are in adb_direct.c. 195 */ 196extern u_char *adbBuffer; /* pointer to user data area */ 197extern adbComp *adbCompRout; /* pointer to the completion routine */ 198extern volatile int *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 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}; 215extern void adb_pass_up(struct adbCommand *); 216 217#if 0 218/* 219 * Define the external functions 220 */ 221extern int zshard(int); /* from zs.c */ 222#endif 223 224#ifdef ADB_DEBUG 225/* 226 * This function dumps contents of the PMData 227 */ 228void 229pm_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 */ 245void 246pm_setup_adb() 247{ 248} 249 250/* 251 * Search for targ in list. list is an area of listlen bytes 252 * containing null-terminated strings. 253 */ 254static int 255strinlist(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 */ 276void 277pm_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 */ 338void 339pm_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 */ 351int 352pm_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 */ 372int 373pm_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 */ 394static int 395pm_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 */ 435static int 436pm_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 */ 469int 470pmgrop(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 inetrrupts 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 */ 568int 569pm_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 happend */ 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 */ 646int 647pm_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 768void 769pm_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 796void 797pm_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 823void 824pm_adb_restart() 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 835void 836pm_adb_poweroff() 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 848void 849pm_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 862void 863pm_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 874int 875pm_read_brightness() 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 888void 889pm_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 906void 907pm_init_brightness() 908{ 909 int val; 910 911 val = pm_read_brightness(); 912 pm_set_brightness(val); 913} 914 915void 916pm_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 */ 934static int 935pm_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 984static int 985pm_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 1049int 1050pm_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 1068int 1069pm_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 1083void 1084pm_write_nvram(addr, val) 1085 int addr, val; 1086{ 1087 PMData p; 1088 1089 p.command = PMU_WRITE_NVRAM; 1090 p.num_data = 3; 1091 p.s_buf = p.r_buf = p.data; 1092 p.data[0] = addr >> 8; 1093 p.data[1] = addr; 1094 p.data[2] = val; 1095 pmgrop(&p); 1096} 1097