pm_direct.c revision 1.23
1/* $NetBSD: pm_direct.c,v 1.23 2005/02/01 02:46:00 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.23 2005/02/01 02:46:00 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#define HwCfgFlags3 0x20000 /* XXX */ 71 72/* define the types of the Power Manager */ 73#define PM_HW_UNKNOWN 0x00 /* don't know */ 74#define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */ 75#define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */ 76 77/* useful macros */ 78#define PM_SR() read_via_reg(VIA1, vSR) 79#define PM_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x90) 80#define PM_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x10) 81#define PM_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x90) 82#if 0 83#define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x04) 84#define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x04) 85#define PM_IS_ON (0x02 == (read_via_reg(VIA2, vBufB) & 0x02)) 86#define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x02)) 87#else 88#define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x10) 89#define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x10) 90#define PM_IS_ON (0x08 == (read_via_reg(VIA2, vBufB) & 0x08)) 91#define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x08)) 92#endif 93 94/* 95 * Variables for internal use 96 */ 97int pmHardware = PM_HW_UNKNOWN; 98u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */ 99u_int pm_LCD_brightness = 0x0; 100u_int pm_LCD_contrast = 0x0; 101u_int pm_counter = 0; /* clock count */ 102 103static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type; 104static int pmu_nbatt; 105static int strinlist(char *, char *, int); 106static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type; 107 108/* these values shows that number of data returned after 'send' cmd is sent */ 109signed char pm_send_cmd_type[] = { 110 -1, -1, -1, -1, -1, -1, -1, -1, 111 -1, -1, -1, -1, -1, -1, -1, -1, 112 0x01, 0x01, -1, -1, -1, -1, -1, -1, 113 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, 114 -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, 115 0x00, -1, -1, -1, -1, -1, -1, -1, 116 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, 117 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, 118 0x01, 0x01, -1, -1, -1, -1, -1, -1, 119 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, 120 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, 121 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, 122 0x02, -1, -1, -1, -1, -1, -1, -1, 123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, 124 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, 125 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04, 126 0x04, -1, 0x00, -1, -1, -1, -1, -1, 127 0x00, -1, -1, -1, -1, -1, -1, -1, 128 0x01, 0x02, -1, -1, -1, -1, -1, -1, 129 0x00, 0x00, -1, -1, -1, -1, -1, -1, 130 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, 131 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, 132 -1, -1, -1, -1, -1, -1, -1, -1, 133 -1, -1, -1, -1, -1, -1, -1, -1, 134 -1, -1, -1, -1, -1, -1, -1, -1, 135 -1, -1, -1, -1, -1, -1, -1, -1, 136 0x00, -1, -1, -1, -1, -1, -1, -1, 137 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, 138 -1, 0x04, 0x00, -1, -1, -1, -1, -1, 139 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, 140 -1, -1, -1, -1, -1, -1, -1, -1, 141 -1, -1, -1, -1, -1, -1, -1, -1 142}; 143 144/* these values shows that number of data returned after 'receive' cmd is sent */ 145signed char pm_receive_cmd_type[] = { 146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 -1, -1, -1, -1, -1, -1, -1, -1, 148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, 150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 -1, -1, -1, -1, -1, -1, -1, -1, 152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 153 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, 154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 0x02, 0x02, -1, -1, -1, -1, -1, -1, 156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, 158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, 160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 -1, -1, -1, -1, -1, -1, 0x01, 0x01, 162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 0x06, -1, -1, -1, -1, -1, -1, -1, 164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 0x02, 0x02, -1, -1, -1, -1, -1, -1, 166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 167 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, 168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 -1, -1, -1, -1, -1, -1, -1, -1, 170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 171 -1, -1, -1, -1, -1, -1, -1, -1, 172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, 174 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 175 -1, -1, 0x02, -1, -1, -1, -1, 0x00, 176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 177 -1, -1, -1, -1, -1, -1, -1, -1, 178}; 179 180 181/* 182 * Define the private functions 183 */ 184 185/* for debugging */ 186#ifdef ADB_DEBUG 187void pm_printerr __P((char *, int, int, char *)); 188#endif 189 190int pm_wait_busy __P((int)); 191int pm_wait_free __P((int)); 192 193/* these functions are for the PB1XX series */ 194int pm_receive_pm1 __P((u_char *)); 195int pm_send_pm1 __P((u_char,int)); 196int pm_pmgrop_pm1 __P((PMData *)); 197void pm_intr_pm1 __P((void)); 198 199/* these functions are for the PB Duo series and the PB 5XX series */ 200int pm_receive_pm2 __P((u_char *)); 201int pm_send_pm2 __P((u_char)); 202int pm_pmgrop_pm2 __P((PMData *)); 203void pm_intr_pm2 __P((void)); 204 205/* these functions are called from adb_direct.c */ 206void pm_setup_adb __P((void)); 207void pm_check_adb_devices __P((int)); 208void pm_intr __P((void)); 209int pm_adb_op __P((u_char *, void *, void *, int)); 210 211/* these functions also use the variables of adb_direct.c */ 212void pm_adb_get_TALK_result __P((PMData *)); 213void pm_adb_get_ADB_data __P((PMData *)); 214void pm_adb_poll_next_device_pm1 __P((PMData *)); 215 216 217/* 218 * These variables are in adb_direct.c. 219 */ 220extern u_char *adbBuffer; /* pointer to user data area */ 221extern void *adbCompRout; /* pointer to the completion routine */ 222extern void *adbCompData; /* pointer to the completion routine data */ 223extern int adbWaiting; /* waiting for return data from the device */ 224extern int adbWaitingCmd; /* ADB command we are waiting for */ 225extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ 226 227#define ADB_MAX_MSG_LENGTH 16 228#define ADB_MAX_HDR_LENGTH 8 229struct adbCommand { 230 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ 231 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ 232 u_char *saveBuf; /* where to save result */ 233 u_char *compRout; /* completion routine pointer */ 234 u_char *compData; /* completion routine data pointer */ 235 u_int cmd; /* the original command for this data */ 236 u_int unsol; /* 1 if packet was unsolicited */ 237 u_int ack_only; /* 1 for no special processing */ 238}; 239extern void adb_pass_up __P((struct adbCommand *)); 240 241#if 0 242/* 243 * Define the external functions 244 */ 245extern int zshard __P((int)); /* from zs.c */ 246#endif 247 248#ifdef ADB_DEBUG 249/* 250 * This function dumps contents of the PMData 251 */ 252void 253pm_printerr(ttl, rval, num, data) 254 char *ttl; 255 int rval; 256 int num; 257 char *data; 258{ 259 int i; 260 261 printf("pm: %s:%04x %02x ", ttl, rval, num); 262 for (i = 0; i < num; i++) 263 printf("%02x ", data[i]); 264 printf("\n"); 265} 266#endif 267 268 269 270/* 271 * Check the hardware type of the Power Manager 272 */ 273void 274pm_setup_adb() 275{ 276 pmHardware = PM_HW_PB5XX; /* XXX */ 277} 278 279static int 280strinlist(char *targ, char *list, int listlen) 281{ 282 char *str; 283 int sl; 284 285 str = list; 286 while (listlen > 0) { 287 sl = strlen(str); 288 if (strncmp(targ, str, sl) == 0) 289 return 1; 290 str += sl+1; 291 listlen -= sl+1; 292 } 293 return 0; 294} 295 296/* 297 * Check the hardware type of the Power Manager 298 */ 299void 300pm_init(void) 301{ 302 uint32_t regs[10]; 303 PMData pmdata; 304 char compat[128]; 305 int clen, node, imask; 306 307 node = OF_peer(0); 308 if (node == -1) { 309 printf("pmu: Failed to get root"); 310 return; 311 } 312 clen = OF_getprop(node, "compatible", compat, sizeof(compat)); 313 if (clen <= 0) { 314 printf("pmu: failed to read root compatible data %d\n", clen); 315 return; 316 } 317 318 imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK; 319 320 if (strinlist("AAPL,3500", compat, clen) || 321 strinlist("AAPL,3400/2400", compat, clen)) { 322 /* How to distinguish BATT_COMET? */ 323 pmu_nbatt = 1; 324 pmu_batt_type = BATT_HOOPER; 325 pmu_type = PMU_OHARE; 326 } else if (strinlist("AAPL,PowerBook1998", compat, clen) || 327 strinlist("PowerBook1,1", compat, clen)) { 328 pmu_nbatt = 2; 329 pmu_batt_type = BATT_SMART; 330 pmu_type = PMU_G3; 331 } else { 332 pmu_nbatt = 1; 333 pmu_batt_type = BATT_SMART; 334 pmu_type = PMU_KEYLARGO; 335 node = getnodebyname(0, "power-mgt"); 336 if (node == -1) { 337 printf("pmu: can't find power-mgt\n"); 338 return; 339 } 340 clen = OF_getprop(node, "prim-info", regs, sizeof(regs)); 341 if (clen < 24) { 342 printf("pmu: failed to read prim-info\n"); 343 return; 344 } 345 pmu_nbatt = regs[6] >> 16; 346 } 347 348 pmdata.command = PMU_SET_IMASK; 349 pmdata.num_data = 1; 350 pmdata.s_buf = pmdata.data; 351 pmdata.r_buf = pmdata.data; 352 pmdata.data[0] = imask; 353 pmgrop(&pmdata); 354} 355 356 357/* 358 * Check the existent ADB devices 359 */ 360void 361pm_check_adb_devices(id) 362 int id; 363{ 364 u_short ed = 0x1; 365 366 ed <<= id; 367 pm_existent_ADB_devices |= ed; 368} 369 370 371/* 372 * Wait until PM IC is busy 373 */ 374int 375pm_wait_busy(delay) 376 int delay; 377{ 378 while (PM_IS_ON) { 379#ifdef PM_GRAB_SI 380#if 0 381 zshard(0); /* grab any serial interrupts */ 382#else 383 (void)intr_dispatch(0x70); 384#endif 385#endif 386 if ((--delay) < 0) 387 return 1; /* timeout */ 388 } 389 return 0; 390} 391 392 393/* 394 * Wait until PM IC is free 395 */ 396int 397pm_wait_free(delay) 398 int delay; 399{ 400 while (PM_IS_OFF) { 401#ifdef PM_GRAB_SI 402#if 0 403 zshard(0); /* grab any serial interrupts */ 404#else 405 (void)intr_dispatch(0x70); 406#endif 407#endif 408 if ((--delay) < 0) 409 return 0; /* timeout */ 410 } 411 return 1; 412} 413 414 415 416/* 417 * Functions for the PB1XX series 418 */ 419 420/* 421 * Receive data from PM for the PB1XX series 422 */ 423int 424pm_receive_pm1(data) 425 u_char *data; 426{ 427#if 0 428 int rval = 0xffffcd34; 429 430 via_reg(VIA2, vDirA) = 0x00; 431 432 switch (1) { 433 default: 434 if (pm_wait_busy(0x40) != 0) 435 break; /* timeout */ 436 437 PM_SET_STATE_ACKOFF(); 438 *data = via_reg(VIA2, 0x200); 439 440 rval = 0xffffcd33; 441 if (pm_wait_free(0x40) == 0) 442 break; /* timeout */ 443 444 rval = 0x00; 445 break; 446 } 447 448 PM_SET_STATE_ACKON(); 449 via_reg(VIA2, vDirA) = 0x00; 450 451 return rval; 452#else 453 panic("pm_receive_pm1"); 454#endif 455} 456 457 458 459/* 460 * Send data to PM for the PB1XX series 461 */ 462int 463pm_send_pm1(data, delay) 464 u_char data; 465 int delay; 466{ 467#if 0 468 int rval; 469 470 via_reg(VIA2, vDirA) = 0xff; 471 via_reg(VIA2, 0x200) = data; 472 473 PM_SET_STATE_ACKOFF(); 474 if (pm_wait_busy(0x400) != 0) { 475 PM_SET_STATE_ACKON(); 476 via_reg(VIA2, vDirA) = 0x00; 477 478 return 0xffffcd36; 479 } 480 481 rval = 0x0; 482 PM_SET_STATE_ACKON(); 483 if (pm_wait_free(0x40) == 0) 484 rval = 0xffffcd35; 485 486 PM_SET_STATE_ACKON(); 487 via_reg(VIA2, vDirA) = 0x00; 488 489 return rval; 490#else 491 panic("pm_send_pm1"); 492#endif 493} 494 495 496/* 497 * My PMgrOp routine for the PB1XX series 498 */ 499int 500pm_pmgrop_pm1(pmdata) 501 PMData *pmdata; 502{ 503#if 0 504 int i; 505 int s = 0x81815963; 506 u_char via1_vIER, via1_vDirA; 507 int rval = 0; 508 int num_pm_data = 0; 509 u_char pm_cmd; 510 u_char pm_data; 511 u_char *pm_buf; 512 513 /* disable all inetrrupts but PM */ 514 via1_vIER = via_reg(VIA1, vIER); 515 PM_VIA_INTR_DISABLE(); 516 517 via1_vDirA = via_reg(VIA1, vDirA); 518 519 switch (pmdata->command) { 520 default: 521 for (i = 0; i < 7; i++) { 522 via_reg(VIA2, vDirA) = 0x00; 523 524 /* wait until PM is free */ 525 if (pm_wait_free(ADBDelay) == 0) { /* timeout */ 526 via_reg(VIA2, vDirA) = 0x00; 527 /* restore formar value */ 528 via_reg(VIA1, vDirA) = via1_vDirA; 529 via_reg(VIA1, vIER) = via1_vIER; 530 return 0xffffcd38; 531 } 532 533 switch (mac68k_machine.machineid) { 534 case MACH_MACPB160: 535 case MACH_MACPB165: 536 case MACH_MACPB165C: 537 case MACH_MACPB180: 538 case MACH_MACPB180C: 539 { 540 int delay = ADBDelay * 16; 541 542 via_reg(VIA2, vDirA) = 0x00; 543 while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0)) 544 delay--; 545 546 if (delay < 0) { /* timeout */ 547 via_reg(VIA2, vDirA) = 0x00; 548 /* restore formar value */ 549 via_reg(VIA1, vIER) = via1_vIER; 550 return 0xffffcd38; 551 } 552 } 553 } /* end switch */ 554 555 s = splhigh(); 556 557 via1_vDirA = via_reg(VIA1, vDirA); 558 via_reg(VIA1, vDirA) &= 0x7f; 559 560 pm_cmd = (u_char)(pmdata->command & 0xff); 561 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0) 562 break; /* send command succeeded */ 563 564 via_reg(VIA1, vDirA) = via1_vDirA; 565 splx(s); 566 } /* end for */ 567 568 /* failed to send a command */ 569 if (i == 7) { 570 via_reg(VIA2, vDirA) = 0x00; 571 /* restore formar value */ 572 via_reg(VIA1, vDirA) = via1_vDirA; 573 via_reg(VIA1, vIER) = via1_vIER; 574 if (s != 0x81815963) 575 splx(s); 576 return 0xffffcd38; 577 } 578 579 /* send # of PM data */ 580 num_pm_data = pmdata->num_data; 581 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0) 582 break; /* timeout */ 583 584 /* send PM data */ 585 pm_buf = (u_char *)pmdata->s_buf; 586 for (i = 0; i < num_pm_data; i++) 587 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0) 588 break; /* timeout */ 589 if ((i != num_pm_data) && (num_pm_data != 0)) 590 break; /* timeout */ 591 592 /* Will PM IC return data? */ 593 if ((pm_cmd & 0x08) == 0) { 594 rval = 0; 595 break; /* no returned data */ 596 } 597 598 rval = 0xffffcd37; 599 if (pm_wait_busy(ADBDelay) != 0) 600 break; /* timeout */ 601 602 /* receive PM command */ 603 if ((rval = pm_receive_pm1(&pm_data)) != 0) 604 break; 605 606 pmdata->command = pm_data; 607 608 /* receive number of PM data */ 609 if ((rval = pm_receive_pm1(&pm_data)) != 0) 610 break; /* timeout */ 611 num_pm_data = pm_data; 612 pmdata->num_data = num_pm_data; 613 614 /* receive PM data */ 615 pm_buf = (u_char *)pmdata->r_buf; 616 for (i = 0; i < num_pm_data; i++) { 617 if ((rval = pm_receive_pm1(&pm_data)) != 0) 618 break; /* timeout */ 619 pm_buf[i] = pm_data; 620 } 621 622 rval = 0; 623 } 624 625 via_reg(VIA2, vDirA) = 0x00; 626 627 /* restore formar value */ 628 via_reg(VIA1, vDirA) = via1_vDirA; 629 via_reg(VIA1, vIER) = via1_vIER; 630 if (s != 0x81815963) 631 splx(s); 632 633 return rval; 634#else 635 panic("pm_pmgrop_pm1"); 636#endif 637} 638 639 640/* 641 * My PM interrupt routine for PB1XX series 642 */ 643void 644pm_intr_pm1() 645{ 646#if 0 647 int s; 648 int rval; 649 PMData pmdata; 650 651 s = splhigh(); 652 653 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 654 655 /* ask PM what happend */ 656 pmdata.command = PMU_INT_ACK; 657 pmdata.num_data = 0; 658 pmdata.data[0] = pmdata.data[1] = 0; 659 pmdata.s_buf = &pmdata.data[2]; 660 pmdata.r_buf = &pmdata.data[2]; 661 rval = pm_pmgrop_pm1(&pmdata); 662 if (rval != 0) { 663#ifdef ADB_DEBUG 664 if (adb_debug) 665 printf("pm: PM is not ready. error code=%08x\n", rval); 666#endif 667 splx(s); 668 } 669 670 if ((pmdata.data[2] & 0x10) == 0x10) { 671 if ((pmdata.data[2] & 0x0f) == 0) { 672 /* ADB data that were requested by TALK command */ 673 pm_adb_get_TALK_result(&pmdata); 674 } else if ((pmdata.data[2] & 0x08) == 0x8) { 675 /* PM is requesting to poll */ 676 pm_adb_poll_next_device_pm1(&pmdata); 677 } else if ((pmdata.data[2] & 0x04) == 0x4) { 678 /* ADB device event */ 679 pm_adb_get_ADB_data(&pmdata); 680 } 681 } else { 682#ifdef ADB_DEBUG 683 if (adb_debug) 684 pm_printerr("driver does not support this event.", 685 rval, pmdata.num_data, pmdata.data); 686#endif 687 } 688 689 splx(s); 690#else 691 panic("pm_intr_pm1"); 692#endif 693} 694 695 696 697/* 698 * Functions for the PB Duo series and the PB 5XX series 699 */ 700 701/* 702 * Receive data from PM for the PB Duo series and the PB 5XX series 703 */ 704int 705pm_receive_pm2(data) 706 u_char *data; 707{ 708 int i; 709 int rval; 710 711 rval = 0xffffcd34; 712 713 switch (1) { 714 default: 715 /* set VIA SR to input mode */ 716 via_reg_or(VIA1, vACR, 0x0c); 717 via_reg_and(VIA1, vACR, ~0x10); 718 i = PM_SR(); 719 720 PM_SET_STATE_ACKOFF(); 721 if (pm_wait_busy((int)ADBDelay*32) != 0) 722 break; /* timeout */ 723 724 PM_SET_STATE_ACKON(); 725 rval = 0xffffcd33; 726 if (pm_wait_free((int)ADBDelay*32) == 0) 727 break; /* timeout */ 728 729 *data = PM_SR(); 730 rval = 0; 731 732 break; 733 } 734 735 PM_SET_STATE_ACKON(); 736 via_reg_or(VIA1, vACR, 0x1c); 737 738 return rval; 739} 740 741 742 743/* 744 * Send data to PM for the PB Duo series and the PB 5XX series 745 */ 746int 747pm_send_pm2(data) 748 u_char data; 749{ 750 int rval; 751 752 via_reg_or(VIA1, vACR, 0x1c); 753 write_via_reg(VIA1, vSR, data); /* PM_SR() = data; */ 754 755 PM_SET_STATE_ACKOFF(); 756 rval = 0xffffcd36; 757 if (pm_wait_busy((int)ADBDelay*32) != 0) { 758 PM_SET_STATE_ACKON(); 759 760 via_reg_or(VIA1, vACR, 0x1c); 761 762 return rval; 763 } 764 765 PM_SET_STATE_ACKON(); 766 rval = 0xffffcd35; 767 if (pm_wait_free((int)ADBDelay*32) != 0) 768 rval = 0; 769 770 PM_SET_STATE_ACKON(); 771 via_reg_or(VIA1, vACR, 0x1c); 772 773 return rval; 774} 775 776 777 778/* 779 * My PMgrOp routine for the PB Duo series and the PB 5XX series 780 */ 781int 782pm_pmgrop_pm2(pmdata) 783 PMData *pmdata; 784{ 785 int i; 786 int s; 787 u_char via1_vIER; 788 int rval = 0; 789 int num_pm_data = 0; 790 u_char pm_cmd; 791 short pm_num_rx_data; 792 u_char pm_data; 793 u_char *pm_buf; 794 795 s = splhigh(); 796 797 /* disable all inetrrupts but PM */ 798 via1_vIER = 0x10; 799 via1_vIER &= read_via_reg(VIA1, vIER); 800 write_via_reg(VIA1, vIER, via1_vIER); 801 if (via1_vIER != 0x0) 802 via1_vIER |= 0x80; 803 804 switch (pmdata->command) { 805 default: 806 /* wait until PM is free */ 807 pm_cmd = (u_char)(pmdata->command & 0xff); 808 rval = 0xcd38; 809 if (pm_wait_free(ADBDelay * 4) == 0) 810 break; /* timeout */ 811 812 if (HwCfgFlags3 & 0x00200000) { 813 /* PB 160, PB 165(c), PB 180(c)? */ 814 int delay = ADBDelay * 16; 815 816 write_via_reg(VIA2, vDirA, 0x00); 817 while ((read_via_reg(VIA2, 0x200) == 0x07) && 818 (delay >= 0)) 819 delay--; 820 821 if (delay < 0) { 822 rval = 0xffffcd38; 823 break; /* timeout */ 824 } 825 } 826 827 /* send PM command */ 828 if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff)))) 829 break; /* timeout */ 830 831 /* send number of PM data */ 832 num_pm_data = pmdata->num_data; 833 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 834 if (pm_send_cmd_type[pm_cmd] < 0) { 835 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 836 break; /* timeout */ 837 pmdata->command = 0; 838 } 839 } else { /* PB 1XX series ? */ 840 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 841 break; /* timeout */ 842 } 843 /* send PM data */ 844 pm_buf = (u_char *)pmdata->s_buf; 845 for (i = 0 ; i < num_pm_data; i++) 846 if ((rval = pm_send_pm2(pm_buf[i])) != 0) 847 break; /* timeout */ 848 if (i != num_pm_data) 849 break; /* timeout */ 850 851 852 /* check if PM will send me data */ 853 pm_num_rx_data = pm_receive_cmd_type[pm_cmd]; 854 pmdata->num_data = pm_num_rx_data; 855 if (pm_num_rx_data == 0) { 856 rval = 0; 857 break; /* no return data */ 858 } 859 860 /* receive PM command */ 861 pm_data = pmdata->command; 862 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 863 pm_num_rx_data--; 864 if (pm_num_rx_data == 0) 865 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 866 rval = 0xffffcd37; 867 break; 868 } 869 pmdata->command = pm_data; 870 } else { /* PB 1XX series ? */ 871 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 872 rval = 0xffffcd37; 873 break; 874 } 875 pmdata->command = pm_data; 876 } 877 878 /* receive number of PM data */ 879 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 880 if (pm_num_rx_data < 0) { 881 if ((rval = pm_receive_pm2(&pm_data)) != 0) 882 break; /* timeout */ 883 num_pm_data = pm_data; 884 } else 885 num_pm_data = pm_num_rx_data; 886 pmdata->num_data = num_pm_data; 887 } else { /* PB 1XX serias ? */ 888 if ((rval = pm_receive_pm2(&pm_data)) != 0) 889 break; /* timeout */ 890 num_pm_data = pm_data; 891 pmdata->num_data = num_pm_data; 892 } 893 894 /* receive PM data */ 895 pm_buf = (u_char *)pmdata->r_buf; 896 for (i = 0; i < num_pm_data; i++) { 897 if ((rval = pm_receive_pm2(&pm_data)) != 0) 898 break; /* timeout */ 899 pm_buf[i] = pm_data; 900 } 901 902 rval = 0; 903 } 904 905 /* restore former value */ 906 write_via_reg(VIA1, vIER, via1_vIER); 907 splx(s); 908 909 return rval; 910} 911 912 913/* 914 * My PM interrupt routine for the PB Duo series and the PB 5XX series 915 */ 916void 917pm_intr_pm2() 918{ 919 int s; 920 int rval; 921 PMData pmdata; 922 923 s = splhigh(); 924 925 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 926 /* ask PM what happend */ 927 pmdata.command = PMU_INT_ACK; 928 pmdata.num_data = 0; 929 pmdata.s_buf = &pmdata.data[2]; 930 pmdata.r_buf = &pmdata.data[2]; 931 rval = pm_pmgrop_pm2(&pmdata); 932 if (rval != 0) { 933#ifdef ADB_DEBUG 934 if (adb_debug) 935 printf("pm: PM is not ready. error code: %08x\n", rval); 936#endif 937 splx(s); 938 return; 939 } 940 941 switch ((u_int)(pmdata.data[2] & 0xff)) { 942 case 0x00: /* no event pending? */ 943 break; 944 case 0x80: /* 1 sec interrupt? */ 945 pm_counter++; 946 break; 947 case 0x08: /* Brightness/Contrast button on LCD panel */ 948 /* get brightness and contrast of the LCD */ 949 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff; 950 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff; 951/* 952 pm_printerr("#08", rval, pmdata.num_data, pmdata.data); 953 pmdata.command = 0x33; 954 pmdata.num_data = 1; 955 pmdata.s_buf = pmdata.data; 956 pmdata.r_buf = pmdata.data; 957 pmdata.data[0] = pm_LCD_contrast; 958 rval = pm_pmgrop_pm2(&pmdata); 959 pm_printerr("#33", rval, pmdata.num_data, pmdata.data); 960*/ 961 /* this is an experimental code */ 962 pmdata.command = PMU_SET_BRIGHTNESS; 963 pmdata.num_data = 1; 964 pmdata.s_buf = pmdata.data; 965 pmdata.r_buf = pmdata.data; 966 pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2; 967 if (pm_LCD_brightness < 0x08) 968 pm_LCD_brightness = 0x08; 969 if (pm_LCD_brightness > 0x78) 970 pm_LCD_brightness = 0x78; 971 pmdata.data[0] = pm_LCD_brightness; 972 rval = pm_pmgrop_pm2(&pmdata); 973 break; 974 case 0x10: /* ADB data requested by TALK command */ 975 case 0x14: 976 pm_adb_get_TALK_result(&pmdata); 977 break; 978 case 0x16: /* ADB device event */ 979 case 0x18: 980 case 0x1e: 981 pm_adb_get_ADB_data(&pmdata); 982 break; 983 default: 984#ifdef ADB_DEBUG 985 if (adb_debug) 986 pm_printerr("driver does not support this event.", 987 pmdata.data[2], pmdata.num_data, 988 pmdata.data); 989#endif 990 break; 991 } 992 993 splx(s); 994} 995 996 997/* 998 * My PMgrOp routine 999 */ 1000int 1001pmgrop(pmdata) 1002 PMData *pmdata; 1003{ 1004 switch (pmHardware) { 1005 case PM_HW_PB1XX: 1006 return (pm_pmgrop_pm1(pmdata)); 1007 break; 1008 case PM_HW_PB5XX: 1009 return (pm_pmgrop_pm2(pmdata)); 1010 break; 1011 default: 1012 /* return (pmgrop_mrg(pmdata)); */ 1013 return 1; 1014 } 1015} 1016 1017 1018/* 1019 * My PM interrupt routine 1020 */ 1021void 1022pm_intr() 1023{ 1024 switch (pmHardware) { 1025 case PM_HW_PB1XX: 1026 pm_intr_pm1(); 1027 break; 1028 case PM_HW_PB5XX: 1029 pm_intr_pm2(); 1030 break; 1031 default: 1032 break; 1033 } 1034} 1035 1036 1037 1038/* 1039 * Synchronous ADBOp routine for the Power Manager 1040 */ 1041int 1042pm_adb_op(buffer, compRout, data, command) 1043 u_char *buffer; 1044 void *compRout; 1045 void *data; 1046 int command; 1047{ 1048 int i; 1049 int s; 1050 int rval; 1051 int timo; 1052 PMData pmdata; 1053 struct adbCommand packet; 1054 1055 if (adbWaiting == 1) 1056 return 1; 1057 1058 s = splhigh(); 1059 write_via_reg(VIA1, vIER, 0x10); 1060 1061 adbBuffer = buffer; 1062 adbCompRout = compRout; 1063 adbCompData = data; 1064 1065 pmdata.command = PMU_ADB_CMD; 1066 pmdata.s_buf = pmdata.data; 1067 pmdata.r_buf = pmdata.data; 1068 1069 /* if the command is LISTEN, add number of ADB data to number of PM data */ 1070 if ((command & 0xc) == 0x8) { 1071 if (buffer != (u_char *)0) 1072 pmdata.num_data = buffer[0] + 3; 1073 } else { 1074 pmdata.num_data = 3; 1075 } 1076 1077 pmdata.data[0] = (u_char)(command & 0xff); 1078 pmdata.data[1] = 0; 1079 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ 1080 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { 1081 pmdata.data[2] = buffer[0]; /* number of data */ 1082 for (i = 0; i < buffer[0]; i++) 1083 pmdata.data[3 + i] = buffer[1 + i]; 1084 } else 1085 pmdata.data[2] = 0; 1086 } else 1087 pmdata.data[2] = 0; 1088 1089 if ((command & 0xc) != 0xc) { /* if the command is not TALK */ 1090 /* set up stuff for adb_pass_up */ 1091 packet.data[0] = 1 + pmdata.data[2]; 1092 packet.data[1] = command; 1093 for (i = 0; i < pmdata.data[2]; i++) 1094 packet.data[i+2] = pmdata.data[i+3]; 1095 packet.saveBuf = adbBuffer; 1096 packet.compRout = adbCompRout; 1097 packet.compData = adbCompData; 1098 packet.cmd = command; 1099 packet.unsol = 0; 1100 packet.ack_only = 1; 1101 adb_polling = 1; 1102 adb_pass_up(&packet); 1103 adb_polling = 0; 1104 } 1105 1106 rval = pmgrop(&pmdata); 1107 if (rval != 0) { 1108 splx(s); 1109 return 1; 1110 } 1111 1112 delay(10000); 1113 1114 adbWaiting = 1; 1115 adbWaitingCmd = command; 1116 1117 PM_VIA_INTR_ENABLE(); 1118 1119 /* wait until the PM interrupt has occurred */ 1120 timo = 0x80000; 1121 while (adbWaiting == 1) { 1122 if (read_via_reg(VIA1, vIFR) & 0x14) 1123 pm_intr(); 1124#ifdef PM_GRAB_SI 1125#if 0 1126 zshard(0); /* grab any serial interrupts */ 1127#else 1128 (void)intr_dispatch(0x70); 1129#endif 1130#endif 1131 if ((--timo) < 0) { 1132 /* Try to take an interrupt anyway, just in case. 1133 * This has been observed to happen on my ibook 1134 * when i press a key after boot and before adb 1135 * is attached; For example, when booting with -d. 1136 */ 1137 pm_intr(); 1138 if (adbWaiting) { 1139 printf("pm_adb_op: timeout. command = 0x%x\n",command); 1140 splx(s); 1141 return 1; 1142 } 1143#ifdef ADB_DEBUG 1144 else { 1145 printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command); 1146 } 1147#endif 1148 } 1149 } 1150 1151 /* this command enables the interrupt by operating ADB devices */ 1152 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */ 1153 pmdata.command = PMU_ADB_CMD; 1154 pmdata.num_data = 4; 1155 pmdata.s_buf = pmdata.data; 1156 pmdata.r_buf = pmdata.data; 1157 pmdata.data[0] = 0x00; 1158 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */ 1159 pmdata.data[2] = 0x00; 1160 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */ 1161 } else { /* PB 1XX series */ 1162 pmdata.command = PMU_ADB_CMD; 1163 pmdata.num_data = 3; 1164 pmdata.s_buf = pmdata.data; 1165 pmdata.r_buf = pmdata.data; 1166 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc; 1167 pmdata.data[1] = 0x04; 1168 pmdata.data[2] = 0x00; 1169 } 1170 rval = pmgrop(&pmdata); 1171 1172 splx(s); 1173 return rval; 1174} 1175 1176 1177void 1178pm_adb_get_TALK_result(pmdata) 1179 PMData *pmdata; 1180{ 1181 int i; 1182 struct adbCommand packet; 1183 1184 /* set up data for adb_pass_up */ 1185 packet.data[0] = pmdata->num_data-1; 1186 packet.data[1] = pmdata->data[3]; 1187 for (i = 0; i <packet.data[0]-1; i++) 1188 packet.data[i+2] = pmdata->data[i+4]; 1189 1190 packet.saveBuf = adbBuffer; 1191 packet.compRout = adbCompRout; 1192 packet.compData = adbCompData; 1193 packet.unsol = 0; 1194 packet.ack_only = 0; 1195 adb_polling = 1; 1196 adb_pass_up(&packet); 1197 adb_polling = 0; 1198 1199 adbWaiting = 0; 1200 adbBuffer = (long)0; 1201 adbCompRout = (long)0; 1202 adbCompData = (long)0; 1203} 1204 1205 1206void 1207pm_adb_get_ADB_data(pmdata) 1208 PMData *pmdata; 1209{ 1210 int i; 1211 struct adbCommand packet; 1212 1213 if (pmu_type == PMU_OHARE && pmdata->num_data == 4 && 1214 pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff && 1215 ((pmdata->data[2] & ~1) == 0xf4)) { 1216 if (pmdata->data[2] == 0xf4) { 1217 pm_eject_pcmcia(0); 1218 } else { 1219 pm_eject_pcmcia(1); 1220 } 1221 return; 1222 } 1223 /* set up data for adb_pass_up */ 1224 packet.data[0] = pmdata->num_data-1; /* number of raw data */ 1225 packet.data[1] = pmdata->data[3]; /* ADB command */ 1226 for (i = 0; i <packet.data[0]-1; i++) 1227 packet.data[i+2] = pmdata->data[i+4]; 1228 packet.unsol = 1; 1229 packet.ack_only = 0; 1230 adb_pass_up(&packet); 1231} 1232 1233 1234void 1235pm_adb_poll_next_device_pm1(pmdata) 1236 PMData *pmdata; 1237{ 1238 int i; 1239 int ndid; 1240 u_short bendid = 0x1; 1241 int rval; 1242 PMData tmp_pmdata; 1243 1244 /* find another existent ADB device to poll */ 1245 for (i = 1; i < 16; i++) { 1246 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf; 1247 bendid <<= ndid; 1248 if ((pm_existent_ADB_devices & bendid) != 0) 1249 break; 1250 } 1251 1252 /* poll the other device */ 1253 tmp_pmdata.command = PMU_ADB_CMD; 1254 tmp_pmdata.num_data = 3; 1255 tmp_pmdata.s_buf = tmp_pmdata.data; 1256 tmp_pmdata.r_buf = tmp_pmdata.data; 1257 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; 1258 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ 1259 tmp_pmdata.data[2] = 0x00; 1260 rval = pmgrop(&tmp_pmdata); 1261} 1262 1263void 1264pm_adb_restart() 1265{ 1266 PMData p; 1267 1268 p.command = PMU_RESET_CPU; 1269 p.num_data = 0; 1270 p.s_buf = p.data; 1271 p.r_buf = p.data; 1272 pmgrop(&p); 1273} 1274 1275void 1276pm_adb_poweroff() 1277{ 1278 PMData p; 1279 1280 p.command = PMU_POWER_OFF; 1281 p.num_data = 4; 1282 p.s_buf = p.data; 1283 p.r_buf = p.data; 1284 strcpy(p.data, "MATT"); 1285 pmgrop(&p); 1286} 1287 1288void 1289pm_read_date_time(time) 1290 u_long *time; 1291{ 1292 PMData p; 1293 1294 p.command = PMU_READ_RTC; 1295 p.num_data = 0; 1296 p.s_buf = p.data; 1297 p.r_buf = p.data; 1298 pmgrop(&p); 1299 1300 memcpy(time, p.data, 4); 1301} 1302 1303void 1304pm_set_date_time(time) 1305 u_long time; 1306{ 1307 PMData p; 1308 1309 p.command = PMU_SET_RTC; 1310 p.num_data = 4; 1311 p.s_buf = p.r_buf = p.data; 1312 memcpy(p.data, &time, 4); 1313 pmgrop(&p); 1314} 1315 1316int 1317pm_read_brightness() 1318{ 1319 PMData p; 1320 1321 p.command = PMU_READ_BRIGHTNESS; 1322 p.num_data = 1; /* XXX why 1? */ 1323 p.s_buf = p.r_buf = p.data; 1324 p.data[0] = 0; 1325 pmgrop(&p); 1326 1327 return p.data[0]; 1328} 1329 1330void 1331pm_set_brightness(val) 1332 int val; 1333{ 1334 PMData p; 1335 1336 val = 0x7f - val / 2; 1337 if (val < 0x08) 1338 val = 0x08; 1339 if (val > 0x78) 1340 val = 0x78; 1341 1342 p.command = PMU_SET_BRIGHTNESS; 1343 p.num_data = 1; 1344 p.s_buf = p.r_buf = p.data; 1345 p.data[0] = val; 1346 pmgrop(&p); 1347} 1348 1349void 1350pm_init_brightness() 1351{ 1352 int val; 1353 1354 val = pm_read_brightness(); 1355 pm_set_brightness(val); 1356} 1357 1358void 1359pm_eject_pcmcia(slot) 1360 int slot; 1361{ 1362 PMData p; 1363 1364 if (slot != 0 && slot != 1) 1365 return; 1366 1367 p.command = PMU_EJECT_PCMCIA; 1368 p.num_data = 1; 1369 p.s_buf = p.r_buf = p.data; 1370 p.data[0] = 5 + slot; /* XXX */ 1371 pmgrop(&p); 1372} 1373 1374/* 1375 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation 1376 * for a clear description of the PMU results. 1377 */ 1378static int 1379pm_battery_info_smart(int battery, struct pmu_battery_info *info) 1380{ 1381 PMData p; 1382 1383 p.command = PMU_SMART_BATTERY_STATE; 1384 p.num_data = 1; 1385 p.s_buf = p.r_buf = p.data; 1386 p.data[0] = battery + 1; 1387 pmgrop(&p); 1388 1389 info->flags = p.data[1]; 1390 1391 info->secs_remaining = 0; 1392 switch (p.data[0]) { 1393 case 3: 1394 case 4: 1395 info->cur_charge = p.data[2]; 1396 info->max_charge = p.data[3]; 1397 info->draw = *((signed char *)&p.data[4]); 1398 info->voltage = p.data[5]; 1399 break; 1400 case 5: 1401 info->cur_charge = ((p.data[2] << 8) | (p.data[3])); 1402 info->max_charge = ((p.data[4] << 8) | (p.data[5])); 1403 info->draw = *((signed short *)&p.data[6]); 1404 info->voltage = ((p.data[8] << 8) | (p.data[7])); 1405 break; 1406 default: 1407 /* XXX - Error condition */ 1408 info->cur_charge = 0; 1409 info->max_charge = 0; 1410 info->draw = 0; 1411 info->voltage = 0; 1412 break; 1413 } 1414 if (info->draw) { 1415 if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) { 1416 info->secs_remaining = 1417 ((info->max_charge - info->cur_charge) * 3600) 1418 / info->draw; 1419 } else { 1420 info->secs_remaining = 1421 (info->cur_charge * 3600) / -info->draw; 1422 } 1423 } 1424 1425 return 1; 1426} 1427 1428static int 1429pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty) 1430{ 1431 PMData p; 1432 long pcharge=0, charge, vb, vmax, lmax; 1433 long vmax_charging, vmax_charged, amperage, voltage; 1434 1435 p.command = PMU_BATTERY_STATE; 1436 p.num_data = 0; 1437 p.s_buf = p.r_buf = p.data; 1438 pmgrop(&p); 1439 1440 info->flags = p.data[0]; 1441 1442 if (info->flags & PMU_PWR_BATT_PRESENT) { 1443 if (ty == BATT_COMET) { 1444 vmax_charging = 213; 1445 vmax_charged = 189; 1446 lmax = 6500; 1447 } else { 1448 /* Experimental values */ 1449 vmax_charging = 365; 1450 vmax_charged = 365; 1451 lmax = 6500; 1452 } 1453 vmax = vmax_charged; 1454 vb = (p.data[1] << 8) | p.data[2]; 1455 voltage = (vb * 256 + 72665) / 10; 1456 amperage = (unsigned char) p.data[5]; 1457 if ((info->flags & PMU_PWR_AC_PRESENT) == 0) { 1458 if (amperage > 200) 1459 vb += ((amperage - 200) * 15)/100; 1460 } else if (info->flags & PMU_PWR_BATT_CHARGING) { 1461 vb = (vb * 97) / 100; 1462 vmax = vmax_charging; 1463 } 1464 charge = (100 * vb) / vmax; 1465 if (info->flags & PMU_PWR_PCHARGE_RESET) { 1466 pcharge = (p.data[6] << 8) | p.data[7]; 1467 if (pcharge > lmax) 1468 pcharge = lmax; 1469 pcharge *= 100; 1470 pcharge = 100 - pcharge / lmax; 1471 if (pcharge < charge) 1472 charge = pcharge; 1473 } 1474 info->cur_charge = charge; 1475 info->max_charge = 100; 1476 info->draw = -amperage; 1477 info->voltage = voltage; 1478 if (amperage > 0) 1479 info->secs_remaining = (charge * 16440) / amperage; 1480 else 1481 info->secs_remaining = 0; 1482 } else { 1483 info->cur_charge = 0; 1484 info->max_charge = 0; 1485 info->draw = 0; 1486 info->voltage = 0; 1487 info->secs_remaining = 0; 1488 } 1489 1490 return 1; 1491} 1492 1493int 1494pm_battery_info(int battery, struct pmu_battery_info *info) 1495{ 1496 1497 if (battery > pmu_nbatt) 1498 return 0; 1499 1500 switch (pmu_batt_type) { 1501 case BATT_COMET: 1502 case BATT_HOOPER: 1503 return pm_battery_info_legacy(battery, info, pmu_batt_type); 1504 1505 case BATT_SMART: 1506 return pm_battery_info_smart(battery, info); 1507 } 1508 1509 return 0; 1510} 1511 1512int 1513pm_read_nvram(addr) 1514 int addr; 1515{ 1516 PMData p; 1517 1518 p.command = PMU_READ_NVRAM; 1519 p.num_data = 2; 1520 p.s_buf = p.r_buf = p.data; 1521 p.data[0] = addr >> 8; 1522 p.data[1] = addr; 1523 pmgrop(&p); 1524 1525 return p.data[0]; 1526} 1527 1528void 1529pm_write_nvram(addr, val) 1530 int addr, val; 1531{ 1532 PMData p; 1533 1534 p.command = PMU_WRITE_NVRAM; 1535 p.num_data = 3; 1536 p.s_buf = p.r_buf = p.data; 1537 p.data[0] = addr >> 8; 1538 p.data[1] = addr; 1539 p.data[2] = val; 1540 pmgrop(&p); 1541} 1542