pm_direct.c revision 1.24
1/* $NetBSD: pm_direct.c,v 1.24 2005/02/01 03:08:16 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.24 2005/02/01 03:08:16 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 *)); 197int 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 *)); 203int 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)); 208int pm_adb_op __P((u_char *, void *, void *, int)); 209 210/* these functions also use the variables of adb_direct.c */ 211void pm_adb_get_TALK_result __P((PMData *)); 212void pm_adb_get_ADB_data __P((PMData *)); 213void pm_adb_poll_next_device_pm1 __P((PMData *)); 214 215 216/* 217 * These variables are in adb_direct.c. 218 */ 219extern u_char *adbBuffer; /* pointer to user data area */ 220extern void *adbCompRout; /* pointer to the completion routine */ 221extern void *adbCompData; /* pointer to the completion routine data */ 222extern int adbWaiting; /* waiting for return data from the device */ 223extern int adbWaitingCmd; /* ADB command we are waiting for */ 224extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ 225 226#define ADB_MAX_MSG_LENGTH 16 227#define ADB_MAX_HDR_LENGTH 8 228struct adbCommand { 229 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ 230 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ 231 u_char *saveBuf; /* where to save result */ 232 u_char *compRout; /* completion routine pointer */ 233 u_char *compData; /* completion routine data pointer */ 234 u_int cmd; /* the original command for this data */ 235 u_int unsol; /* 1 if packet was unsolicited */ 236 u_int ack_only; /* 1 for no special processing */ 237}; 238extern void adb_pass_up __P((struct adbCommand *)); 239 240#if 0 241/* 242 * Define the external functions 243 */ 244extern int zshard __P((int)); /* from zs.c */ 245#endif 246 247#ifdef ADB_DEBUG 248/* 249 * This function dumps contents of the PMData 250 */ 251void 252pm_printerr(ttl, rval, num, data) 253 char *ttl; 254 int rval; 255 int num; 256 char *data; 257{ 258 int i; 259 260 printf("pm: %s:%04x %02x ", ttl, rval, num); 261 for (i = 0; i < num; i++) 262 printf("%02x ", data[i]); 263 printf("\n"); 264} 265#endif 266 267 268 269/* 270 * Check the hardware type of the Power Manager 271 */ 272void 273pm_setup_adb() 274{ 275 pmHardware = PM_HW_PB5XX; /* XXX */ 276} 277 278static int 279strinlist(char *targ, char *list, int listlen) 280{ 281 char *str; 282 int sl; 283 284 str = list; 285 while (listlen > 0) { 286 sl = strlen(str); 287 if (strncmp(targ, str, sl) == 0) 288 return 1; 289 str += sl+1; 290 listlen -= sl+1; 291 } 292 return 0; 293} 294 295/* 296 * Check the hardware type of the Power Manager 297 */ 298void 299pm_init(void) 300{ 301 uint32_t regs[10]; 302 PMData pmdata; 303 char compat[128]; 304 int clen, node, imask; 305 306 node = OF_peer(0); 307 if (node == -1) { 308 printf("pmu: Failed to get root"); 309 return; 310 } 311 clen = OF_getprop(node, "compatible", compat, sizeof(compat)); 312 if (clen <= 0) { 313 printf("pmu: failed to read root compatible data %d\n", clen); 314 return; 315 } 316 317 imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK; 318 319 if (strinlist("AAPL,3500", compat, clen) || 320 strinlist("AAPL,3400/2400", compat, clen)) { 321 /* How to distinguish BATT_COMET? */ 322 pmu_nbatt = 1; 323 pmu_batt_type = BATT_HOOPER; 324 pmu_type = PMU_OHARE; 325 } else if (strinlist("AAPL,PowerBook1998", compat, clen) || 326 strinlist("PowerBook1,1", compat, clen)) { 327 pmu_nbatt = 2; 328 pmu_batt_type = BATT_SMART; 329 pmu_type = PMU_G3; 330 } else { 331 pmu_nbatt = 1; 332 pmu_batt_type = BATT_SMART; 333 pmu_type = PMU_KEYLARGO; 334 node = getnodebyname(0, "power-mgt"); 335 if (node == -1) { 336 printf("pmu: can't find power-mgt\n"); 337 return; 338 } 339 clen = OF_getprop(node, "prim-info", regs, sizeof(regs)); 340 if (clen < 24) { 341 printf("pmu: failed to read prim-info\n"); 342 return; 343 } 344 pmu_nbatt = regs[6] >> 16; 345 } 346 347 pmdata.command = PMU_SET_IMASK; 348 pmdata.num_data = 1; 349 pmdata.s_buf = pmdata.data; 350 pmdata.r_buf = pmdata.data; 351 pmdata.data[0] = imask; 352 pmgrop(&pmdata); 353} 354 355 356/* 357 * Check the existent ADB devices 358 */ 359void 360pm_check_adb_devices(id) 361 int id; 362{ 363 u_short ed = 0x1; 364 365 ed <<= id; 366 pm_existent_ADB_devices |= ed; 367} 368 369 370/* 371 * Wait until PM IC is busy 372 */ 373int 374pm_wait_busy(delay) 375 int delay; 376{ 377 while (PM_IS_ON) { 378#ifdef PM_GRAB_SI 379#if 0 380 zshard(0); /* grab any serial interrupts */ 381#else 382 (void)intr_dispatch(0x70); 383#endif 384#endif 385 if ((--delay) < 0) 386 return 1; /* timeout */ 387 } 388 return 0; 389} 390 391 392/* 393 * Wait until PM IC is free 394 */ 395int 396pm_wait_free(delay) 397 int delay; 398{ 399 while (PM_IS_OFF) { 400#ifdef PM_GRAB_SI 401#if 0 402 zshard(0); /* grab any serial interrupts */ 403#else 404 (void)intr_dispatch(0x70); 405#endif 406#endif 407 if ((--delay) < 0) 408 return 0; /* timeout */ 409 } 410 return 1; 411} 412 413 414 415/* 416 * Functions for the PB1XX series 417 */ 418 419/* 420 * Receive data from PM for the PB1XX series 421 */ 422int 423pm_receive_pm1(data) 424 u_char *data; 425{ 426#if 0 427 int rval = 0xffffcd34; 428 429 via_reg(VIA2, vDirA) = 0x00; 430 431 switch (1) { 432 default: 433 if (pm_wait_busy(0x40) != 0) 434 break; /* timeout */ 435 436 PM_SET_STATE_ACKOFF(); 437 *data = via_reg(VIA2, 0x200); 438 439 rval = 0xffffcd33; 440 if (pm_wait_free(0x40) == 0) 441 break; /* timeout */ 442 443 rval = 0x00; 444 break; 445 } 446 447 PM_SET_STATE_ACKON(); 448 via_reg(VIA2, vDirA) = 0x00; 449 450 return rval; 451#else 452 panic("pm_receive_pm1"); 453#endif 454} 455 456 457 458/* 459 * Send data to PM for the PB1XX series 460 */ 461int 462pm_send_pm1(data, delay) 463 u_char data; 464 int delay; 465{ 466#if 0 467 int rval; 468 469 via_reg(VIA2, vDirA) = 0xff; 470 via_reg(VIA2, 0x200) = data; 471 472 PM_SET_STATE_ACKOFF(); 473 if (pm_wait_busy(0x400) != 0) { 474 PM_SET_STATE_ACKON(); 475 via_reg(VIA2, vDirA) = 0x00; 476 477 return 0xffffcd36; 478 } 479 480 rval = 0x0; 481 PM_SET_STATE_ACKON(); 482 if (pm_wait_free(0x40) == 0) 483 rval = 0xffffcd35; 484 485 PM_SET_STATE_ACKON(); 486 via_reg(VIA2, vDirA) = 0x00; 487 488 return rval; 489#else 490 panic("pm_send_pm1"); 491#endif 492} 493 494 495/* 496 * My PMgrOp routine for the PB1XX series 497 */ 498int 499pm_pmgrop_pm1(pmdata) 500 PMData *pmdata; 501{ 502#if 0 503 int i; 504 int s = 0x81815963; 505 u_char via1_vIER, via1_vDirA; 506 int rval = 0; 507 int num_pm_data = 0; 508 u_char pm_cmd; 509 u_char pm_data; 510 u_char *pm_buf; 511 512 /* disable all inetrrupts but PM */ 513 via1_vIER = via_reg(VIA1, vIER); 514 PM_VIA_INTR_DISABLE(); 515 516 via1_vDirA = via_reg(VIA1, vDirA); 517 518 switch (pmdata->command) { 519 default: 520 for (i = 0; i < 7; i++) { 521 via_reg(VIA2, vDirA) = 0x00; 522 523 /* wait until PM is free */ 524 if (pm_wait_free(ADBDelay) == 0) { /* timeout */ 525 via_reg(VIA2, vDirA) = 0x00; 526 /* restore formar value */ 527 via_reg(VIA1, vDirA) = via1_vDirA; 528 via_reg(VIA1, vIER) = via1_vIER; 529 return 0xffffcd38; 530 } 531 532 switch (mac68k_machine.machineid) { 533 case MACH_MACPB160: 534 case MACH_MACPB165: 535 case MACH_MACPB165C: 536 case MACH_MACPB180: 537 case MACH_MACPB180C: 538 { 539 int delay = ADBDelay * 16; 540 541 via_reg(VIA2, vDirA) = 0x00; 542 while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0)) 543 delay--; 544 545 if (delay < 0) { /* timeout */ 546 via_reg(VIA2, vDirA) = 0x00; 547 /* restore formar value */ 548 via_reg(VIA1, vIER) = via1_vIER; 549 return 0xffffcd38; 550 } 551 } 552 } /* end switch */ 553 554 s = splhigh(); 555 556 via1_vDirA = via_reg(VIA1, vDirA); 557 via_reg(VIA1, vDirA) &= 0x7f; 558 559 pm_cmd = (u_char)(pmdata->command & 0xff); 560 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0) 561 break; /* send command succeeded */ 562 563 via_reg(VIA1, vDirA) = via1_vDirA; 564 splx(s); 565 } /* end for */ 566 567 /* failed to send a command */ 568 if (i == 7) { 569 via_reg(VIA2, vDirA) = 0x00; 570 /* restore formar value */ 571 via_reg(VIA1, vDirA) = via1_vDirA; 572 via_reg(VIA1, vIER) = via1_vIER; 573 if (s != 0x81815963) 574 splx(s); 575 return 0xffffcd38; 576 } 577 578 /* send # of PM data */ 579 num_pm_data = pmdata->num_data; 580 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0) 581 break; /* timeout */ 582 583 /* send PM data */ 584 pm_buf = (u_char *)pmdata->s_buf; 585 for (i = 0; i < num_pm_data; i++) 586 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0) 587 break; /* timeout */ 588 if ((i != num_pm_data) && (num_pm_data != 0)) 589 break; /* timeout */ 590 591 /* Will PM IC return data? */ 592 if ((pm_cmd & 0x08) == 0) { 593 rval = 0; 594 break; /* no returned data */ 595 } 596 597 rval = 0xffffcd37; 598 if (pm_wait_busy(ADBDelay) != 0) 599 break; /* timeout */ 600 601 /* receive PM command */ 602 if ((rval = pm_receive_pm1(&pm_data)) != 0) 603 break; 604 605 pmdata->command = pm_data; 606 607 /* receive number of PM data */ 608 if ((rval = pm_receive_pm1(&pm_data)) != 0) 609 break; /* timeout */ 610 num_pm_data = pm_data; 611 pmdata->num_data = num_pm_data; 612 613 /* receive PM data */ 614 pm_buf = (u_char *)pmdata->r_buf; 615 for (i = 0; i < num_pm_data; i++) { 616 if ((rval = pm_receive_pm1(&pm_data)) != 0) 617 break; /* timeout */ 618 pm_buf[i] = pm_data; 619 } 620 621 rval = 0; 622 } 623 624 via_reg(VIA2, vDirA) = 0x00; 625 626 /* restore formar value */ 627 via_reg(VIA1, vDirA) = via1_vDirA; 628 via_reg(VIA1, vIER) = via1_vIER; 629 if (s != 0x81815963) 630 splx(s); 631 632 return rval; 633#else 634 panic("pm_pmgrop_pm1"); 635#endif 636} 637 638 639/* 640 * My PM interrupt routine for PB1XX series 641 */ 642int 643pm_intr_pm1(void *arg) 644{ 645#if 0 646 int s; 647 int rval; 648 PMData pmdata; 649 650 s = splhigh(); 651 652 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 653 654 /* ask PM what happend */ 655 pmdata.command = PMU_INT_ACK; 656 pmdata.num_data = 0; 657 pmdata.data[0] = pmdata.data[1] = 0; 658 pmdata.s_buf = &pmdata.data[2]; 659 pmdata.r_buf = &pmdata.data[2]; 660 rval = pm_pmgrop_pm1(&pmdata); 661 if (rval != 0) { 662#ifdef ADB_DEBUG 663 if (adb_debug) 664 printf("pm: PM is not ready. error code=%08x\n", rval); 665#endif 666 splx(s); 667 } 668 669 if ((pmdata.data[2] & 0x10) == 0x10) { 670 if ((pmdata.data[2] & 0x0f) == 0) { 671 /* ADB data that were requested by TALK command */ 672 pm_adb_get_TALK_result(&pmdata); 673 } else if ((pmdata.data[2] & 0x08) == 0x8) { 674 /* PM is requesting to poll */ 675 pm_adb_poll_next_device_pm1(&pmdata); 676 } else if ((pmdata.data[2] & 0x04) == 0x4) { 677 /* ADB device event */ 678 pm_adb_get_ADB_data(&pmdata); 679 } 680 } else { 681#ifdef ADB_DEBUG 682 if (adb_debug) 683 pm_printerr("driver does not support this event.", 684 rval, pmdata.num_data, pmdata.data); 685#endif 686 } 687 688 splx(s); 689#else 690 panic("pm_intr_pm1"); 691#endif 692 return 1; 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 */ 916int 917pm_intr_pm2(void *arg) 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 0; 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 return 1; 996} 997 998 999/* 1000 * My PMgrOp routine 1001 */ 1002int 1003pmgrop(pmdata) 1004 PMData *pmdata; 1005{ 1006 switch (pmHardware) { 1007 case PM_HW_PB1XX: 1008 return (pm_pmgrop_pm1(pmdata)); 1009 break; 1010 case PM_HW_PB5XX: 1011 return (pm_pmgrop_pm2(pmdata)); 1012 break; 1013 default: 1014 /* return (pmgrop_mrg(pmdata)); */ 1015 return 1; 1016 } 1017} 1018 1019 1020/* 1021 * My PM interrupt routine 1022 */ 1023int 1024pm_intr(void *arg) 1025{ 1026 switch (pmHardware) { 1027 case PM_HW_PB1XX: 1028 return pm_intr_pm1(arg); 1029 break; 1030 case PM_HW_PB5XX: 1031 return pm_intr_pm2(arg); 1032 break; 1033 default: 1034 break; 1035 } 1036 return 0; 1037} 1038 1039 1040 1041/* 1042 * Synchronous ADBOp routine for the Power Manager 1043 */ 1044int 1045pm_adb_op(buffer, compRout, data, command) 1046 u_char *buffer; 1047 void *compRout; 1048 void *data; 1049 int command; 1050{ 1051 int i; 1052 int s; 1053 int rval; 1054 int timo; 1055 PMData pmdata; 1056 struct adbCommand packet; 1057 1058 if (adbWaiting == 1) 1059 return 1; 1060 1061 s = splhigh(); 1062 write_via_reg(VIA1, vIER, 0x10); 1063 1064 adbBuffer = buffer; 1065 adbCompRout = compRout; 1066 adbCompData = data; 1067 1068 pmdata.command = PMU_ADB_CMD; 1069 pmdata.s_buf = pmdata.data; 1070 pmdata.r_buf = pmdata.data; 1071 1072 /* if the command is LISTEN, add number of ADB data to number of PM data */ 1073 if ((command & 0xc) == 0x8) { 1074 if (buffer != (u_char *)0) 1075 pmdata.num_data = buffer[0] + 3; 1076 } else { 1077 pmdata.num_data = 3; 1078 } 1079 1080 pmdata.data[0] = (u_char)(command & 0xff); 1081 pmdata.data[1] = 0; 1082 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ 1083 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { 1084 pmdata.data[2] = buffer[0]; /* number of data */ 1085 for (i = 0; i < buffer[0]; i++) 1086 pmdata.data[3 + i] = buffer[1 + i]; 1087 } else 1088 pmdata.data[2] = 0; 1089 } else 1090 pmdata.data[2] = 0; 1091 1092 if ((command & 0xc) != 0xc) { /* if the command is not TALK */ 1093 /* set up stuff for adb_pass_up */ 1094 packet.data[0] = 1 + pmdata.data[2]; 1095 packet.data[1] = command; 1096 for (i = 0; i < pmdata.data[2]; i++) 1097 packet.data[i+2] = pmdata.data[i+3]; 1098 packet.saveBuf = adbBuffer; 1099 packet.compRout = adbCompRout; 1100 packet.compData = adbCompData; 1101 packet.cmd = command; 1102 packet.unsol = 0; 1103 packet.ack_only = 1; 1104 adb_polling = 1; 1105 adb_pass_up(&packet); 1106 adb_polling = 0; 1107 } 1108 1109 rval = pmgrop(&pmdata); 1110 if (rval != 0) { 1111 splx(s); 1112 return 1; 1113 } 1114 1115 delay(10000); 1116 1117 adbWaiting = 1; 1118 adbWaitingCmd = command; 1119 1120 PM_VIA_INTR_ENABLE(); 1121 1122 /* wait until the PM interrupt has occurred */ 1123 timo = 0x80000; 1124 while (adbWaiting == 1) { 1125 if (read_via_reg(VIA1, vIFR) & 0x14) 1126 pm_intr(NULL); 1127#ifdef PM_GRAB_SI 1128#if 0 1129 zshard(0); /* grab any serial interrupts */ 1130#else 1131 (void)intr_dispatch(0x70); 1132#endif 1133#endif 1134 if ((--timo) < 0) { 1135 /* Try to take an interrupt anyway, just in case. 1136 * This has been observed to happen on my ibook 1137 * when i press a key after boot and before adb 1138 * is attached; For example, when booting with -d. 1139 */ 1140 pm_intr(NULL); 1141 if (adbWaiting) { 1142 printf("pm_adb_op: timeout. command = 0x%x\n",command); 1143 splx(s); 1144 return 1; 1145 } 1146#ifdef ADB_DEBUG 1147 else { 1148 printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command); 1149 } 1150#endif 1151 } 1152 } 1153 1154 /* this command enables the interrupt by operating ADB devices */ 1155 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */ 1156 pmdata.command = PMU_ADB_CMD; 1157 pmdata.num_data = 4; 1158 pmdata.s_buf = pmdata.data; 1159 pmdata.r_buf = pmdata.data; 1160 pmdata.data[0] = 0x00; 1161 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */ 1162 pmdata.data[2] = 0x00; 1163 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */ 1164 } else { /* PB 1XX series */ 1165 pmdata.command = PMU_ADB_CMD; 1166 pmdata.num_data = 3; 1167 pmdata.s_buf = pmdata.data; 1168 pmdata.r_buf = pmdata.data; 1169 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc; 1170 pmdata.data[1] = 0x04; 1171 pmdata.data[2] = 0x00; 1172 } 1173 rval = pmgrop(&pmdata); 1174 1175 splx(s); 1176 return rval; 1177} 1178 1179 1180void 1181pm_adb_get_TALK_result(pmdata) 1182 PMData *pmdata; 1183{ 1184 int i; 1185 struct adbCommand packet; 1186 1187 /* set up data for adb_pass_up */ 1188 packet.data[0] = pmdata->num_data-1; 1189 packet.data[1] = pmdata->data[3]; 1190 for (i = 0; i <packet.data[0]-1; i++) 1191 packet.data[i+2] = pmdata->data[i+4]; 1192 1193 packet.saveBuf = adbBuffer; 1194 packet.compRout = adbCompRout; 1195 packet.compData = adbCompData; 1196 packet.unsol = 0; 1197 packet.ack_only = 0; 1198 adb_polling = 1; 1199 adb_pass_up(&packet); 1200 adb_polling = 0; 1201 1202 adbWaiting = 0; 1203 adbBuffer = (long)0; 1204 adbCompRout = (long)0; 1205 adbCompData = (long)0; 1206} 1207 1208 1209void 1210pm_adb_get_ADB_data(pmdata) 1211 PMData *pmdata; 1212{ 1213 int i; 1214 struct adbCommand packet; 1215 1216 if (pmu_type == PMU_OHARE && pmdata->num_data == 4 && 1217 pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff && 1218 ((pmdata->data[2] & ~1) == 0xf4)) { 1219 if (pmdata->data[2] == 0xf4) { 1220 pm_eject_pcmcia(0); 1221 } else { 1222 pm_eject_pcmcia(1); 1223 } 1224 return; 1225 } 1226 /* set up data for adb_pass_up */ 1227 packet.data[0] = pmdata->num_data-1; /* number of raw data */ 1228 packet.data[1] = pmdata->data[3]; /* ADB command */ 1229 for (i = 0; i <packet.data[0]-1; i++) 1230 packet.data[i+2] = pmdata->data[i+4]; 1231 packet.unsol = 1; 1232 packet.ack_only = 0; 1233 adb_pass_up(&packet); 1234} 1235 1236 1237void 1238pm_adb_poll_next_device_pm1(pmdata) 1239 PMData *pmdata; 1240{ 1241 int i; 1242 int ndid; 1243 u_short bendid = 0x1; 1244 int rval; 1245 PMData tmp_pmdata; 1246 1247 /* find another existent ADB device to poll */ 1248 for (i = 1; i < 16; i++) { 1249 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf; 1250 bendid <<= ndid; 1251 if ((pm_existent_ADB_devices & bendid) != 0) 1252 break; 1253 } 1254 1255 /* poll the other device */ 1256 tmp_pmdata.command = PMU_ADB_CMD; 1257 tmp_pmdata.num_data = 3; 1258 tmp_pmdata.s_buf = tmp_pmdata.data; 1259 tmp_pmdata.r_buf = tmp_pmdata.data; 1260 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; 1261 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ 1262 tmp_pmdata.data[2] = 0x00; 1263 rval = pmgrop(&tmp_pmdata); 1264} 1265 1266void 1267pm_adb_restart() 1268{ 1269 PMData p; 1270 1271 p.command = PMU_RESET_CPU; 1272 p.num_data = 0; 1273 p.s_buf = p.data; 1274 p.r_buf = p.data; 1275 pmgrop(&p); 1276} 1277 1278void 1279pm_adb_poweroff() 1280{ 1281 PMData p; 1282 1283 p.command = PMU_POWER_OFF; 1284 p.num_data = 4; 1285 p.s_buf = p.data; 1286 p.r_buf = p.data; 1287 strcpy(p.data, "MATT"); 1288 pmgrop(&p); 1289} 1290 1291void 1292pm_read_date_time(time) 1293 u_long *time; 1294{ 1295 PMData p; 1296 1297 p.command = PMU_READ_RTC; 1298 p.num_data = 0; 1299 p.s_buf = p.data; 1300 p.r_buf = p.data; 1301 pmgrop(&p); 1302 1303 memcpy(time, p.data, 4); 1304} 1305 1306void 1307pm_set_date_time(time) 1308 u_long time; 1309{ 1310 PMData p; 1311 1312 p.command = PMU_SET_RTC; 1313 p.num_data = 4; 1314 p.s_buf = p.r_buf = p.data; 1315 memcpy(p.data, &time, 4); 1316 pmgrop(&p); 1317} 1318 1319int 1320pm_read_brightness() 1321{ 1322 PMData p; 1323 1324 p.command = PMU_READ_BRIGHTNESS; 1325 p.num_data = 1; /* XXX why 1? */ 1326 p.s_buf = p.r_buf = p.data; 1327 p.data[0] = 0; 1328 pmgrop(&p); 1329 1330 return p.data[0]; 1331} 1332 1333void 1334pm_set_brightness(val) 1335 int val; 1336{ 1337 PMData p; 1338 1339 val = 0x7f - val / 2; 1340 if (val < 0x08) 1341 val = 0x08; 1342 if (val > 0x78) 1343 val = 0x78; 1344 1345 p.command = PMU_SET_BRIGHTNESS; 1346 p.num_data = 1; 1347 p.s_buf = p.r_buf = p.data; 1348 p.data[0] = val; 1349 pmgrop(&p); 1350} 1351 1352void 1353pm_init_brightness() 1354{ 1355 int val; 1356 1357 val = pm_read_brightness(); 1358 pm_set_brightness(val); 1359} 1360 1361void 1362pm_eject_pcmcia(slot) 1363 int slot; 1364{ 1365 PMData p; 1366 1367 if (slot != 0 && slot != 1) 1368 return; 1369 1370 p.command = PMU_EJECT_PCMCIA; 1371 p.num_data = 1; 1372 p.s_buf = p.r_buf = p.data; 1373 p.data[0] = 5 + slot; /* XXX */ 1374 pmgrop(&p); 1375} 1376 1377/* 1378 * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation 1379 * for a clear description of the PMU results. 1380 */ 1381static int 1382pm_battery_info_smart(int battery, struct pmu_battery_info *info) 1383{ 1384 PMData p; 1385 1386 p.command = PMU_SMART_BATTERY_STATE; 1387 p.num_data = 1; 1388 p.s_buf = p.r_buf = p.data; 1389 p.data[0] = battery + 1; 1390 pmgrop(&p); 1391 1392 info->flags = p.data[1]; 1393 1394 info->secs_remaining = 0; 1395 switch (p.data[0]) { 1396 case 3: 1397 case 4: 1398 info->cur_charge = p.data[2]; 1399 info->max_charge = p.data[3]; 1400 info->draw = *((signed char *)&p.data[4]); 1401 info->voltage = p.data[5]; 1402 break; 1403 case 5: 1404 info->cur_charge = ((p.data[2] << 8) | (p.data[3])); 1405 info->max_charge = ((p.data[4] << 8) | (p.data[5])); 1406 info->draw = *((signed short *)&p.data[6]); 1407 info->voltage = ((p.data[8] << 8) | (p.data[7])); 1408 break; 1409 default: 1410 /* XXX - Error condition */ 1411 info->cur_charge = 0; 1412 info->max_charge = 0; 1413 info->draw = 0; 1414 info->voltage = 0; 1415 break; 1416 } 1417 if (info->draw) { 1418 if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) { 1419 info->secs_remaining = 1420 ((info->max_charge - info->cur_charge) * 3600) 1421 / info->draw; 1422 } else { 1423 info->secs_remaining = 1424 (info->cur_charge * 3600) / -info->draw; 1425 } 1426 } 1427 1428 return 1; 1429} 1430 1431static int 1432pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty) 1433{ 1434 PMData p; 1435 long pcharge=0, charge, vb, vmax, lmax; 1436 long vmax_charging, vmax_charged, amperage, voltage; 1437 1438 p.command = PMU_BATTERY_STATE; 1439 p.num_data = 0; 1440 p.s_buf = p.r_buf = p.data; 1441 pmgrop(&p); 1442 1443 info->flags = p.data[0]; 1444 1445 if (info->flags & PMU_PWR_BATT_PRESENT) { 1446 if (ty == BATT_COMET) { 1447 vmax_charging = 213; 1448 vmax_charged = 189; 1449 lmax = 6500; 1450 } else { 1451 /* Experimental values */ 1452 vmax_charging = 365; 1453 vmax_charged = 365; 1454 lmax = 6500; 1455 } 1456 vmax = vmax_charged; 1457 vb = (p.data[1] << 8) | p.data[2]; 1458 voltage = (vb * 256 + 72665) / 10; 1459 amperage = (unsigned char) p.data[5]; 1460 if ((info->flags & PMU_PWR_AC_PRESENT) == 0) { 1461 if (amperage > 200) 1462 vb += ((amperage - 200) * 15)/100; 1463 } else if (info->flags & PMU_PWR_BATT_CHARGING) { 1464 vb = (vb * 97) / 100; 1465 vmax = vmax_charging; 1466 } 1467 charge = (100 * vb) / vmax; 1468 if (info->flags & PMU_PWR_PCHARGE_RESET) { 1469 pcharge = (p.data[6] << 8) | p.data[7]; 1470 if (pcharge > lmax) 1471 pcharge = lmax; 1472 pcharge *= 100; 1473 pcharge = 100 - pcharge / lmax; 1474 if (pcharge < charge) 1475 charge = pcharge; 1476 } 1477 info->cur_charge = charge; 1478 info->max_charge = 100; 1479 info->draw = -amperage; 1480 info->voltage = voltage; 1481 if (amperage > 0) 1482 info->secs_remaining = (charge * 16440) / amperage; 1483 else 1484 info->secs_remaining = 0; 1485 } else { 1486 info->cur_charge = 0; 1487 info->max_charge = 0; 1488 info->draw = 0; 1489 info->voltage = 0; 1490 info->secs_remaining = 0; 1491 } 1492 1493 return 1; 1494} 1495 1496int 1497pm_battery_info(int battery, struct pmu_battery_info *info) 1498{ 1499 1500 if (battery > pmu_nbatt) 1501 return 0; 1502 1503 switch (pmu_batt_type) { 1504 case BATT_COMET: 1505 case BATT_HOOPER: 1506 return pm_battery_info_legacy(battery, info, pmu_batt_type); 1507 1508 case BATT_SMART: 1509 return pm_battery_info_smart(battery, info); 1510 } 1511 1512 return 0; 1513} 1514 1515int 1516pm_read_nvram(addr) 1517 int addr; 1518{ 1519 PMData p; 1520 1521 p.command = PMU_READ_NVRAM; 1522 p.num_data = 2; 1523 p.s_buf = p.r_buf = p.data; 1524 p.data[0] = addr >> 8; 1525 p.data[1] = addr; 1526 pmgrop(&p); 1527 1528 return p.data[0]; 1529} 1530 1531void 1532pm_write_nvram(addr, val) 1533 int addr, val; 1534{ 1535 PMData p; 1536 1537 p.command = PMU_WRITE_NVRAM; 1538 p.num_data = 3; 1539 p.s_buf = p.r_buf = p.data; 1540 p.data[0] = addr >> 8; 1541 p.data[1] = addr; 1542 p.data[2] = val; 1543 pmgrop(&p); 1544} 1545