1 /* $NetBSD: pm_direct.c,v 1.37 2025/01/12 05:53:45 nat Exp $ */ 2 3 /* 4 * Copyright (c) 2024 Nathanial Sloss <nathanialsloss@yahoo.com.au> 5 * All rights reserved. 6 * 7 * Copyright (C) 1997 Takashi Hamada 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Takashi Hamada 21 * 4. The name of the author may not be used to endorse or promote products 22 * derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 /* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.37 2025/01/12 05:53:45 nat Exp $"); 39 40 #include "opt_adb.h" 41 42 #ifdef DEBUG 43 #ifndef ADB_DEBUG 44 #define ADB_DEBUG 45 #endif 46 #endif 47 48 /* #define PM_GRAB_SI 1 */ 49 50 #include <sys/types.h> 51 #include <sys/systm.h> 52 53 #include <dev/sysmon/sysmonvar.h> 54 55 #include <machine/viareg.h> 56 #include <machine/param.h> 57 #include <machine/cpu.h> 58 #include <machine/adbsys.h> 59 60 #include <mac68k/mac68k/macrom.h> 61 #include <mac68k/dev/adbvar.h> 62 #include <mac68k/dev/pm_direct.h> 63 64 /* hardware dependent values */ 65 extern u_short ADBDelay; 66 extern u_int32_t HwCfgFlags3; 67 extern struct mac68k_machine_S mac68k_machine; 68 69 70 /* define the types of the Power Manager */ 71 #define PM_HW_UNKNOWN 0x00 /* don't know */ 72 #define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */ 73 #define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */ 74 75 /* useful macros */ 76 #define PM_SR() via_reg(VIA1, vSR) 77 #define PM_VIA_INTR_ENABLE() via_reg(VIA1, vIER) = 0x90 78 #define PM_VIA_INTR_DISABLE() via_reg(VIA1, vIER) = 0x10 79 #define PM_VIA_CLR_INTR() via_reg(VIA1, vIFR) = 0x90 80 #define PM_SET_STATE_ACKON() via_reg(VIA2, vBufB) |= 0x04 81 #define PM_SET_STATE_ACKOFF() via_reg(VIA2, vBufB) &= ~0x04 82 #define PM_IS_ON (0x02 == (via_reg(VIA2, vBufB) & 0x02)) 83 #define PM_IS_OFF (0x00 == (via_reg(VIA2, vBufB) & 0x02)) 84 85 /* 86 * Variables for internal use 87 */ 88 int pmHardware = PM_HW_UNKNOWN; 89 u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */ 90 u_int pm_LCD_brightness = 0x0; 91 u_int pm_LCD_contrast = 0x0; 92 u_int pm_counter = 0; /* clock count */ 93 struct sysmon_pswitch pbutton; 94 95 /* these values shows that number of data returned after 'send' cmd is sent */ 96 char pm_send_cmd_type[] = { 97 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 98 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 99 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 100 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 101 0xff, 0x00, 0x02, 0x01, 0x01, 0xff, 0xff, 0xff, 102 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 103 0x04, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 104 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 105 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 106 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 107 0x01, 0x00, 0x02, 0x02, 0xff, 0x01, 0x03, 0x01, 108 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 109 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 111 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 112 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x04, 0x04, 113 0x04, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 114 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 115 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 116 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 117 0x02, 0x02, 0x02, 0x04, 0xff, 0x00, 0xff, 0xff, 118 0x01, 0x01, 0x03, 0x02, 0xff, 0xff, 0xff, 0xff, 119 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 121 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 122 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 123 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 124 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 125 0xff, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 126 0x03, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 127 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 128 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 129 }; 130 131 /* these values shows that number of data returned after 'receive' cmd is sent */ 132 char pm_receive_cmd_type[] = { 133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 136 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 138 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 140 0x05, 0x15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 0x02, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 146 0x04, 0x04, 0x03, 0x09, 0xff, 0xff, 0xff, 0xff, 147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 152 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 160 0x02, 0x02, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 161 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 162 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 165 }; 166 167 168 /* 169 * Define the private functions 170 */ 171 172 /* for debugging */ 173 #ifdef ADB_DEBUG 174 void pm_printerr(const char *, int, int, char *); 175 #endif 176 177 int pm_wait_busy(int); 178 int pm_wait_free(int); 179 180 /* these functions are for the PB1XX series */ 181 int pm_receive_pm1(u_char *); 182 int pm_send_pm1(u_char, int); 183 int pm_pmgrop_pm1(PMData *); 184 void pm_intr_pm1(void *); 185 186 /* these functions are for the PB Duo series and the PB 5XX series */ 187 int pm_receive_pm2(u_char *); 188 int pm_send_pm2(u_char); 189 int pm_pmgrop_pm2(PMData *); 190 void pm_intr_pm2(void *); 191 192 /* this function is MRG-Based (for testing) */ 193 int pm_pmgrop_mrg(PMData *); 194 195 /* these functions are called from adb_direct.c */ 196 void pm_setup_adb(void); 197 void pm_check_adb_devices(int); 198 void pm_intr(void *); 199 int pm_adb_op(u_char *, void *, void *, int); 200 void pm_hw_setup(void); 201 202 /* these functions also use the variables of adb_direct.c */ 203 void pm_adb_get_TALK_result(PMData *); 204 void pm_adb_get_ADB_data(PMData *); 205 void pm_adb_poll_next_device_pm1(PMData *); 206 207 208 /* 209 * These variables are in adb_direct.c. 210 */ 211 extern u_char *adbBuffer; /* pointer to user data area */ 212 extern void *adbCompRout; /* pointer to the completion routine */ 213 extern void *adbCompData; /* pointer to the completion routine data */ 214 extern int adbWaiting; /* waiting for return data from the device */ 215 extern int adbWaitingCmd; /* ADB command we are waiting for */ 216 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ 217 218 #define ADB_MAX_MSG_LENGTH 16 219 #define ADB_MAX_HDR_LENGTH 8 220 struct adbCommand { 221 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ 222 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ 223 u_char *saveBuf; /* where to save result */ 224 u_char *compRout; /* completion routine pointer */ 225 u_char *compData; /* completion routine data pointer */ 226 u_int cmd; /* the original command for this data */ 227 u_int unsol; /* 1 if packet was unsolicited */ 228 u_int ack_only; /* 1 for no special processing */ 229 }; 230 extern void adb_pass_up(struct adbCommand *); 231 232 #ifdef ADB_DEBUG 233 /* 234 * This function dumps contents of the PMData 235 */ 236 void 237 pm_printerr(const char *ttl, int rval, int num, char *data) 238 { 239 int i; 240 241 printf("pm: %s:%04x %02x ", ttl, rval, num); 242 for (i = 0; i < num; i++) 243 printf("%02x ", data[i]); 244 printf("\n"); 245 } 246 #endif 247 248 249 250 /* 251 * Check the hardware type of the Power Manager 252 */ 253 void 254 pm_setup_adb(void) 255 { 256 switch (mac68k_machine.machineid) { 257 case MACH_MACPB140: 258 case MACH_MACPB145: 259 case MACH_MACPB160: 260 case MACH_MACPB165: 261 case MACH_MACPB165C: 262 case MACH_MACPB170: 263 case MACH_MACPB180: 264 case MACH_MACPB180C: 265 pmHardware = PM_HW_PB1XX; 266 267 memset(&pbutton, 0, sizeof(struct sysmon_pswitch)); 268 pbutton.smpsw_name = "PB"; 269 pbutton.smpsw_type = PSWITCH_TYPE_POWER; 270 if (sysmon_pswitch_register(&pbutton) != 0) 271 printf("Unable to register soft power\n"); 272 break; 273 case MACH_MACPB150: 274 case MACH_MACPB210: 275 case MACH_MACPB230: 276 case MACH_MACPB250: 277 case MACH_MACPB270: 278 case MACH_MACPB280: 279 case MACH_MACPB280C: 280 case MACH_MACPB500: 281 case MACH_MACPB190: 282 case MACH_MACPB190CS: 283 pmHardware = PM_HW_PB5XX; 284 #if notyet 285 memset(&pbutton, 0, sizeof(struct sysmon_pswitch)); 286 pbutton.smpsw_name = "PB"; 287 pbutton.smpsw_type = PSWITCH_TYPE_POWER; 288 if (sysmon_pswitch_register(&pbutton) != 0) 289 printf("Unable to register soft power\n"); 290 #endif 291 break; 292 default: 293 break; 294 } 295 } 296 297 298 /* 299 * Check the existent ADB devices 300 */ 301 void 302 pm_check_adb_devices(int id) 303 { 304 u_short ed = 0x1; 305 306 ed <<= id; 307 pm_existent_ADB_devices |= ed; 308 } 309 310 311 /* 312 * Wait until PM IC is busy 313 */ 314 int 315 pm_wait_busy(int xdelay) 316 { 317 while (PM_IS_ON) { 318 #ifdef PM_GRAB_SI 319 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 320 #endif 321 if ((--xdelay) < 0) 322 return 1; /* timeout */ 323 } 324 return 0; 325 } 326 327 328 /* 329 * Wait until PM IC is free 330 */ 331 int 332 pm_wait_free(int xdelay) 333 { 334 while (PM_IS_OFF) { 335 #ifdef PM_GRAB_SI 336 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 337 #endif 338 if ((--xdelay) < 0) 339 return 0; /* timeout */ 340 } 341 return 1; 342 } 343 344 345 346 /* 347 * Functions for the PB1XX series 348 */ 349 350 /* 351 * Receive data from PM for the PB1XX series 352 */ 353 int 354 pm_receive_pm1(u_char *data) 355 { 356 int rval = 0xffffcd34; 357 358 via_reg(VIA2, vDirA) = 0x00; 359 360 switch (1) { 361 default: 362 if (pm_wait_busy(0x40) != 0) 363 break; /* timeout */ 364 365 PM_SET_STATE_ACKOFF(); 366 *data = via_reg(VIA2, 0x200); 367 368 rval = 0xffffcd33; 369 if (pm_wait_free(0x40) == 0) 370 break; /* timeout */ 371 372 rval = 0x00; 373 break; 374 } 375 376 PM_SET_STATE_ACKON(); 377 via_reg(VIA2, vDirA) = 0x00; 378 379 return rval; 380 } 381 382 383 384 /* 385 * Send data to PM for the PB1XX series 386 */ 387 int 388 pm_send_pm1(u_char data, int timo) 389 { 390 int rval; 391 392 via_reg(VIA2, vDirA) = 0xff; 393 via_reg(VIA2, 0x200) = data; 394 395 PM_SET_STATE_ACKOFF(); 396 #if 0 397 if (pm_wait_busy(0x400) == 0) { 398 #else 399 if (pm_wait_busy(timo) == 0) { 400 #endif 401 PM_SET_STATE_ACKON(); 402 if (pm_wait_free(0x40) != 0) 403 rval = 0x0; 404 else 405 rval = 0xffffcd35; 406 } else { 407 rval = 0xffffcd36; 408 } 409 410 PM_SET_STATE_ACKON(); 411 via_reg(VIA2, vDirA) = 0x00; 412 413 return rval; 414 } 415 416 417 /* 418 * My PMgrOp routine for the PB1XX series 419 */ 420 int 421 pm_pmgrop_pm1(PMData *pmdata) 422 { 423 int i; 424 int s = 0x81815963; 425 u_char via1_vIER, via1_vDirA; 426 int rval = 0; 427 int num_pm_data = 0; 428 u_char pm_cmd; 429 u_char pm_data; 430 u_char *pm_buf; 431 432 /* disable all interrupts but PM */ 433 via1_vIER = via_reg(VIA1, vIER); 434 PM_VIA_INTR_DISABLE(); 435 436 via1_vDirA = via_reg(VIA1, vDirA); 437 438 switch (pmdata->command) { 439 default: 440 for (i = 0; i < 7; i++) { 441 via_reg(VIA2, vDirA) = 0x00; 442 443 /* wait until PM is free */ 444 if (pm_wait_free(ADBDelay) == 0) { /* timeout */ 445 via_reg(VIA2, vDirA) = 0x00; 446 /* restore formar value */ 447 via_reg(VIA1, vDirA) = via1_vDirA; 448 via_reg(VIA1, vIER) = via1_vIER; 449 return 0xffffcd38; 450 } 451 452 switch (mac68k_machine.machineid) { 453 case MACH_MACPB160: 454 case MACH_MACPB165: 455 case MACH_MACPB165C: 456 case MACH_MACPB170: 457 case MACH_MACPB180: 458 case MACH_MACPB180C: 459 { 460 int xdelay = ADBDelay * 16; 461 462 via_reg(VIA2, vDirA) = 0x00; 463 while ((via_reg(VIA2, 0x200) == 0x7f) && (xdelay >= 0)) 464 xdelay--; 465 466 if (xdelay < 0) { /* timeout */ 467 via_reg(VIA2, vDirA) = 0x00; 468 /* restore formar value */ 469 via_reg(VIA1, vIER) = via1_vIER; 470 return 0xffffcd38; 471 } 472 } 473 } /* end switch */ 474 475 s = splhigh(); 476 477 via1_vDirA = via_reg(VIA1, vDirA); 478 via_reg(VIA1, vDirA) &= 0x7f; 479 480 pm_cmd = (u_char)(pmdata->command & 0xff); 481 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0) 482 break; /* send command succeeded */ 483 484 via_reg(VIA1, vDirA) = via1_vDirA; 485 splx(s); 486 } /* end for */ 487 488 /* failed to send a command */ 489 if (i == 7) { 490 via_reg(VIA2, vDirA) = 0x00; 491 /* restore formar value */ 492 via_reg(VIA1, vDirA) = via1_vDirA; 493 via_reg(VIA1, vIER) = via1_vIER; 494 if (s != 0x81815963) 495 splx(s); 496 return 0xffffcd38; 497 } 498 499 /* send # of PM data */ 500 num_pm_data = pmdata->num_data; 501 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0) 502 break; /* timeout */ 503 504 /* send PM data */ 505 pm_buf = (u_char *)pmdata->s_buf; 506 for (i = 0; i < num_pm_data; i++) 507 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0) 508 break; /* timeout */ 509 if ((i != num_pm_data) && (num_pm_data != 0)) 510 break; /* timeout */ 511 512 /* Will PM IC return data? */ 513 if ((pm_cmd & 0x08) == 0) { 514 rval = 0; 515 break; /* no returned data */ 516 } 517 518 rval = 0xffffcd37; 519 if (pm_wait_busy(ADBDelay) != 0) 520 break; /* timeout */ 521 522 /* receive PM command */ 523 if ((rval = pm_receive_pm1(&pm_data)) != 0) 524 break; 525 526 pmdata->command = pm_data; 527 528 /* receive number of PM data */ 529 if ((rval = pm_receive_pm1(&pm_data)) != 0) 530 break; /* timeout */ 531 num_pm_data = pm_data; 532 pmdata->num_data = num_pm_data; 533 534 /* receive PM data */ 535 pm_buf = (u_char *)pmdata->r_buf; 536 for (i = 0; i < num_pm_data; i++) { 537 if ((rval = pm_receive_pm1(&pm_data)) != 0) 538 break; /* timeout */ 539 pm_buf[i] = pm_data; 540 } 541 542 rval = 0; 543 } 544 545 via_reg(VIA2, vDirA) = 0x00; 546 547 /* restore formar value */ 548 via_reg(VIA1, vDirA) = via1_vDirA; 549 via_reg(VIA1, vIER) = via1_vIER; 550 if (s != 0x81815963) 551 splx(s); 552 553 return rval; 554 } 555 556 557 /* 558 * My PM interrupt routine for PB1XX series 559 */ 560 void 561 pm_intr_pm1(void *arg) 562 { 563 int s; 564 int rval; 565 PMData pmdata; 566 567 s = splhigh(); 568 569 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 570 571 /* ask PM what happened */ 572 pmdata.command = 0x78; 573 pmdata.num_data = 0; 574 pmdata.data[0] = pmdata.data[1] = 0; 575 pmdata.s_buf = &pmdata.data[2]; 576 pmdata.r_buf = &pmdata.data[2]; 577 rval = pm_pmgrop_pm1(&pmdata); 578 if (rval != 0) { 579 #ifdef ADB_DEBUG 580 if (adb_debug) 581 printf("pm: PM is not ready. error code=%08x\n", rval); 582 #endif 583 splx(s); 584 return; 585 } 586 587 if ((pmdata.data[2] & 0x10) == 0x10) { 588 if ((pmdata.data[2] & 0x0f) == 0) { 589 /* ADB data that were requested by TALK command */ 590 pm_adb_get_TALK_result(&pmdata); 591 } else if ((pmdata.data[2] & 0x08) == 0x8) { 592 /* PM is requesting to poll */ 593 pm_adb_poll_next_device_pm1(&pmdata); 594 } else if ((pmdata.data[2] & 0x04) == 0x4) { 595 /* ADB device event */ 596 pm_adb_get_ADB_data(&pmdata); 597 } 598 } else if ((pmdata.num_data == 0x1) && (pmdata.data[0] == 0)) { 599 /* PowerBook 160/180 Power button. */ 600 sysmon_pswitch_event(&pbutton, PSWITCH_EVENT_PRESSED); 601 } else { 602 #ifdef ADB_DEBUG 603 if (adb_debug) 604 pm_printerr("driver does not supported this event.", 605 rval, pmdata.num_data, pmdata.data); 606 #endif 607 } 608 609 splx(s); 610 } 611 612 613 614 /* 615 * Functions for the PB Duo series and the PB 5XX series 616 */ 617 618 /* 619 * Receive data from PM for the PB Duo series and the PB 5XX series 620 */ 621 int 622 pm_receive_pm2(u_char *data) 623 { 624 int rval; 625 626 rval = 0xffffcd34; 627 628 switch (1) { 629 default: 630 /* set VIA SR to input mode */ 631 via_reg(VIA1, vACR) |= 0x0c; 632 via_reg(VIA1, vACR) &= ~0x10; 633 PM_SR(); 634 635 PM_SET_STATE_ACKOFF(); 636 if (pm_wait_busy((int)ADBDelay*32) != 0) 637 break; /* timeout */ 638 639 PM_SET_STATE_ACKON(); 640 rval = 0xffffcd33; 641 if (pm_wait_free((int)ADBDelay*32) == 0) 642 break; /* timeout */ 643 644 *data = PM_SR(); 645 rval = 0; 646 647 break; 648 } 649 650 PM_SET_STATE_ACKON(); 651 via_reg(VIA1, vACR) |= 0x1c; 652 653 return rval; 654 } 655 656 657 658 /* 659 * Send data to PM for the PB Duo series and the PB 5XX series 660 */ 661 int 662 pm_send_pm2(u_char data) 663 { 664 int rval; 665 666 via_reg(VIA1, vACR) |= 0x1c; 667 PM_SR() = data; 668 669 PM_SET_STATE_ACKOFF(); 670 if (pm_wait_busy((int)ADBDelay*32) == 0) { 671 PM_SET_STATE_ACKON(); 672 if (pm_wait_free((int)ADBDelay*32) != 0) 673 rval = 0; 674 else 675 rval = 0xffffcd35; 676 } else { 677 rval = 0xffffcd36; 678 } 679 680 PM_SET_STATE_ACKON(); 681 via_reg(VIA1, vACR) |= 0x1c; 682 683 return rval; 684 } 685 686 687 688 /* 689 * My PMgrOp routine for the PB Duo series and the PB 5XX series 690 */ 691 int 692 pm_pmgrop_pm2(PMData *pmdata) 693 { 694 int i; 695 int s; 696 u_char via1_vIER; 697 int rval = 0; 698 int num_pm_data = 0; 699 u_char pm_cmd; 700 short pm_num_rx_data; 701 u_char pm_data; 702 u_char *pm_buf; 703 704 s = splhigh(); 705 706 /* disable all interrupts but PM */ 707 via1_vIER = 0x10; 708 via1_vIER &= via_reg(VIA1, vIER); 709 via_reg(VIA1, vIER) = via1_vIER; 710 if (via1_vIER != 0x0) 711 via1_vIER |= 0x80; 712 713 switch (pmdata->command) { 714 default: 715 /* wait until PM is free */ 716 pm_cmd = (u_char)(pmdata->command & 0xff); 717 rval = 0xcd38; 718 if (pm_wait_free(ADBDelay * 4) == 0) 719 break; /* timeout */ 720 721 if (HwCfgFlags3 & 0x00200000) { 722 /* PB 160, PB 165(c), PB 180(c)? */ 723 int xdelay = ADBDelay * 16; 724 725 via_reg(VIA2, vDirA) = 0x00; 726 while ((via_reg(VIA2, 0x200) == 0x07) && 727 (xdelay >= 0)) 728 xdelay--; 729 730 if (xdelay < 0) { 731 rval = 0xffffcd38; 732 break; /* timeout */ 733 } 734 } 735 736 /* send PM command */ 737 if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff)))) 738 break; /* timeout */ 739 740 /* send number of PM data */ 741 num_pm_data = pmdata->num_data; 742 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 743 if (pm_send_cmd_type[pm_cmd] < 0) { 744 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 745 break; /* timeout */ 746 pmdata->command = 0; 747 } 748 } else { /* PB 1XX series ? */ 749 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 750 break; /* timeout */ 751 } 752 /* send PM data */ 753 pm_buf = (u_char *)pmdata->s_buf; 754 for (i = 0 ; i < num_pm_data; i++) 755 if ((rval = pm_send_pm2(pm_buf[i])) != 0) 756 break; /* timeout */ 757 if (i != num_pm_data) 758 break; /* timeout */ 759 760 761 /* check if PM will send me data */ 762 pm_num_rx_data = pm_receive_cmd_type[pm_cmd]; 763 pmdata->num_data = pm_num_rx_data; 764 if (pm_num_rx_data == 0) { 765 rval = 0; 766 break; /* no return data */ 767 } 768 769 /* receive PM command */ 770 pm_data = pmdata->command; 771 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 772 pm_num_rx_data--; 773 if (pm_num_rx_data == 0) 774 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 775 rval = 0xffffcd37; 776 break; 777 } 778 pmdata->command = pm_data; 779 } else { /* PB 1XX series ? */ 780 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 781 rval = 0xffffcd37; 782 break; 783 } 784 pmdata->command = pm_data; 785 } 786 787 /* receive number of PM data */ 788 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 789 if (pm_num_rx_data < 0) { 790 if ((rval = pm_receive_pm2(&pm_data)) != 0) 791 break; /* timeout */ 792 num_pm_data = pm_data; 793 } else 794 num_pm_data = pm_num_rx_data; 795 pmdata->num_data = num_pm_data; 796 } else { /* PB 1XX serias ? */ 797 if ((rval = pm_receive_pm2(&pm_data)) != 0) 798 break; /* timeout */ 799 num_pm_data = pm_data; 800 pmdata->num_data = num_pm_data; 801 } 802 803 /* receive PM data */ 804 pm_buf = (u_char *)pmdata->r_buf; 805 for (i = 0; i < num_pm_data; i++) { 806 if ((rval = pm_receive_pm2(&pm_data)) != 0) 807 break; /* timeout */ 808 pm_buf[i] = pm_data; 809 } 810 811 rval = 0; 812 } 813 814 /* restore former value */ 815 via_reg(VIA1, vIER) = via1_vIER; 816 splx(s); 817 818 return rval; 819 } 820 821 822 /* 823 * My PM interrupt routine for the PB Duo series and the PB 5XX series 824 */ 825 void 826 pm_intr_pm2(void *arg) 827 { 828 int s; 829 int rval; 830 PMData pmdata; 831 832 s = splhigh(); 833 834 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 835 /* ask PM what happened */ 836 pmdata.command = 0x78; 837 pmdata.num_data = 0; 838 pmdata.s_buf = &pmdata.data[2]; 839 pmdata.r_buf = &pmdata.data[2]; 840 rval = pm_pmgrop_pm2(&pmdata); 841 if (rval != 0) { 842 #ifdef ADB_DEBUG 843 if (adb_debug) 844 printf("pm: PM is not ready. error code: %08x\n", rval); 845 #endif 846 splx(s); 847 return; 848 } 849 850 switch ((u_int)(pmdata.data[2] & 0xff)) { 851 case 0x00: /* 1 sec interrupt? */ 852 break; 853 case 0x80: /* 1 sec interrupt? */ 854 pm_counter++; 855 break; 856 case 0x08: /* Brightness/Contrast button on LCD panel */ 857 /* get brightness and contrast of the LCD */ 858 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff; 859 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff; 860 /* 861 pm_printerr("#08", rval, pmdata.num_data, pmdata.data); 862 pmdata.command = 0x33; 863 pmdata.num_data = 1; 864 pmdata.s_buf = pmdata.data; 865 pmdata.r_buf = pmdata.data; 866 pmdata.data[0] = pm_LCD_contrast; 867 rval = pm_pmgrop_pm2(&pmdata); 868 pm_printerr("#33", rval, pmdata.num_data, pmdata.data); 869 */ 870 pm_LCD_brightness = 871 pm_set_brightness(pm_LCD_brightness); 872 break; 873 case 0x10: /* ADB data that were requested by TALK command */ 874 case 0x14: 875 pm_adb_get_TALK_result(&pmdata); 876 break; 877 case 0x16: /* ADB device event */ 878 case 0x18: 879 case 0x1e: 880 pm_adb_get_ADB_data(&pmdata); 881 break; 882 default: 883 #ifdef ADB_DEBUG 884 if (adb_debug) 885 pm_printerr("driver does not supported this event.", 886 pmdata.data[2], pmdata.num_data, 887 pmdata.data); 888 #endif 889 break; 890 } 891 892 splx(s); 893 } 894 895 896 /* 897 * MRG-based PMgrOp routine 898 */ 899 int 900 pm_pmgrop_mrg(PMData *pmdata) 901 { 902 u_int32_t rval=0; 903 904 __asm volatile( 905 " movl %1,%%a0 \n" 906 " .word 0xa085 \n" 907 " movl %%d0,%0" 908 : "=g" (rval) 909 : "g" (pmdata) 910 : "a0","d0"); 911 912 return rval; 913 } 914 915 916 /* 917 * My PMgrOp routine 918 */ 919 int 920 pmgrop(PMData *pmdata) 921 { 922 switch (pmHardware) { 923 case PM_HW_PB1XX: 924 return (pm_pmgrop_pm1(pmdata)); 925 break; 926 case PM_HW_PB5XX: 927 return (pm_pmgrop_pm2(pmdata)); 928 break; 929 default: 930 /* return (pmgrop_mrg(pmdata)); */ 931 return 1; 932 } 933 } 934 935 936 /* 937 * My PM interrupt routine 938 */ 939 void 940 pm_intr(void *arg) 941 { 942 switch (pmHardware) { 943 case PM_HW_PB1XX: 944 pm_intr_pm1(arg); 945 break; 946 case PM_HW_PB5XX: 947 pm_intr_pm2(arg); 948 break; 949 default: 950 break; 951 } 952 } 953 954 955 void 956 pm_hw_setup(void) 957 { 958 switch (pmHardware) { 959 case PM_HW_PB1XX: 960 via1_register_irq(4, pm_intr_pm1, (void *)0); 961 PM_VIA_CLR_INTR(); 962 break; 963 case PM_HW_PB5XX: 964 via1_register_irq(4, pm_intr_pm2, (void *)0); 965 PM_VIA_CLR_INTR(); 966 break; 967 default: 968 break; 969 } 970 } 971 972 973 /* 974 * Synchronous ADBOp routine for the Power Manager 975 */ 976 int 977 pm_adb_op(u_char *buffer, void *compRout, void *data, int command) 978 { 979 int i; 980 int s; 981 int rval; 982 int xdelay; 983 PMData pmdata; 984 struct adbCommand packet; 985 986 if (adbWaiting == 1) 987 return 1; 988 989 s = splhigh(); 990 via_reg(VIA1, vIER) = 0x10; 991 992 adbBuffer = buffer; 993 adbCompRout = compRout; 994 adbCompData = data; 995 996 pmdata.command = 0x20; 997 pmdata.s_buf = pmdata.data; 998 pmdata.r_buf = pmdata.data; 999 1000 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, add number of ADB data to number of PM data */ 1001 if (buffer != (u_char *)0) 1002 pmdata.num_data = buffer[0] + 3; 1003 } else { 1004 pmdata.num_data = 3; 1005 } 1006 1007 pmdata.data[0] = (u_char)(command & 0xff); 1008 pmdata.data[1] = 0; 1009 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ 1010 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { 1011 pmdata.data[2] = buffer[0]; /* number of data */ 1012 for (i = 0; i < buffer[0]; i++) 1013 pmdata.data[3 + i] = buffer[1 + i]; 1014 } else 1015 pmdata.data[2] = 0; 1016 } else 1017 pmdata.data[2] = 0; 1018 1019 if ((command & 0xc) != 0xc) { /* if the command is not TALK */ 1020 /* set up stuff fNULLor adb_pass_up */ 1021 packet.data[0] = 1 + pmdata.data[2]; 1022 packet.data[1] = command; 1023 for (i = 0; i < pmdata.data[2]; i++) 1024 packet.data[i+2] = pmdata.data[i+3]; 1025 packet.saveBuf = adbBuffer; 1026 packet.compRout = adbCompRout; 1027 packet.compData = adbCompData; 1028 packet.cmd = command; 1029 packet.unsol = 0; 1030 packet.ack_only = 1; 1031 adb_polling = 1; 1032 adb_pass_up(&packet); 1033 adb_polling = 0; 1034 } 1035 1036 rval = pmgrop(&pmdata); 1037 if (rval != 0) { 1038 splx(s); 1039 return 1; 1040 } 1041 1042 adbWaiting = 1; 1043 adbWaitingCmd = command; 1044 1045 PM_VIA_INTR_ENABLE(); 1046 1047 /* wait until the PM interrupt has occurred */ 1048 xdelay = 0x80000; 1049 while (adbWaiting == 1) { 1050 switch (mac68k_machine.machineid) { 1051 case MACH_MACPB150: 1052 case MACH_MACPB210: 1053 case MACH_MACPB230: /* daishi tested with Duo230 */ 1054 case MACH_MACPB250: 1055 case MACH_MACPB270: 1056 case MACH_MACPB280: 1057 case MACH_MACPB280C: 1058 case MACH_MACPB190: 1059 case MACH_MACPB190CS: 1060 pm_intr((void *)0); 1061 break; 1062 default: 1063 if ((via_reg(VIA1, vIFR) & 0x10) == 0x10) 1064 pm_intr((void *)0); 1065 break; 1066 } 1067 #ifdef PM_GRAB_SI 1068 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 1069 #endif 1070 if ((--xdelay) < 0) { 1071 splx(s); 1072 return 1; 1073 } 1074 } 1075 1076 /* this command enables the interrupt by operating ADB devices */ 1077 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */ 1078 pmdata.command = 0x20; 1079 pmdata.num_data = 4; 1080 pmdata.s_buf = pmdata.data; 1081 pmdata.r_buf = pmdata.data; 1082 pmdata.data[0] = 0x00; 1083 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */ 1084 pmdata.data[2] = 0x00; 1085 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */ 1086 } else { /* PB 1XX series */ 1087 pmdata.command = 0x20; 1088 pmdata.num_data = 3; 1089 pmdata.s_buf = pmdata.data; 1090 pmdata.r_buf = pmdata.data; 1091 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc; 1092 pmdata.data[1] = 0x04; 1093 pmdata.data[2] = 0x00; 1094 } 1095 rval = pmgrop(&pmdata); 1096 1097 splx(s); 1098 return rval; 1099 } 1100 1101 1102 void 1103 pm_adb_get_TALK_result(PMData *pmdata) 1104 { 1105 int i; 1106 struct adbCommand packet; 1107 1108 /* set up data for adb_pass_up */ 1109 packet.data[0] = pmdata->num_data-1; 1110 packet.data[1] = pmdata->data[3]; 1111 for (i = 0; i <packet.data[0]-1; i++) 1112 packet.data[i+2] = pmdata->data[i+4]; 1113 1114 packet.saveBuf = adbBuffer; 1115 packet.compRout = adbCompRout; 1116 packet.compData = adbCompData; 1117 packet.unsol = 0; 1118 packet.ack_only = 0; 1119 adb_polling = 1; 1120 adb_pass_up(&packet); 1121 adb_polling = 0; 1122 1123 adbWaiting = 0; 1124 adbBuffer = (long)0; 1125 adbCompRout = (long)0; 1126 adbCompData = (long)0; 1127 } 1128 1129 1130 void 1131 pm_adb_get_ADB_data(PMData *pmdata) 1132 { 1133 int i; 1134 struct adbCommand packet; 1135 1136 /* set up data for adb_pass_up */ 1137 packet.data[0] = pmdata->num_data-1; /* number of raw data */ 1138 packet.data[1] = pmdata->data[3]; /* ADB command */ 1139 for (i = 0; i <packet.data[0]-1; i++) 1140 packet.data[i+2] = pmdata->data[i+4]; 1141 packet.unsol = 1; 1142 packet.ack_only = 0; 1143 adb_pass_up(&packet); 1144 } 1145 1146 1147 void 1148 pm_adb_poll_next_device_pm1(PMData *pmdata) 1149 { 1150 int i; 1151 int ndid; 1152 u_short bendid = 0x1; 1153 PMData tmp_pmdata; 1154 1155 /* find another existent ADB device to poll */ 1156 for (i = 1; i < 16; i++) { 1157 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf; 1158 bendid <<= ndid; 1159 if ((pm_existent_ADB_devices & bendid) != 0) 1160 break; 1161 } 1162 1163 /* poll the other device */ 1164 tmp_pmdata.command = 0x20; 1165 tmp_pmdata.num_data = 3; 1166 tmp_pmdata.s_buf = tmp_pmdata.data; 1167 tmp_pmdata.r_buf = tmp_pmdata.data; 1168 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; 1169 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ 1170 tmp_pmdata.data[2] = 0x00; 1171 pmgrop(&tmp_pmdata); 1172 } 1173 1174 void 1175 pm_poweroff(void) 1176 { 1177 PMData pmdata; 1178 int attempt = 3; 1179 1180 while (pmHardware == PM_HW_PB1XX && attempt > 0) { 1181 pmdata.command = 0x7e; 1182 pmdata.num_data = 0; 1183 pmdata.data[0] = pmdata.data[1] = 0; 1184 pmdata.s_buf = &pmdata.data[2]; 1185 pmdata.r_buf = &pmdata.data[2]; 1186 (void)pm_pmgrop_pm1(&pmdata); 1187 attempt--; 1188 } 1189 1190 return; 1191 } 1192 1193 u_int 1194 pm_set_brightness(u_int brightness) 1195 { 1196 PMData pmdata; 1197 1198 pmdata.num_data = 1; 1199 pmdata.s_buf = pmdata.data; 1200 pmdata.r_buf = pmdata.data; 1201 1202 switch (pmHardware) { 1203 case PM_HW_PB5XX: 1204 /* this is an experimental code */ 1205 pmdata.command = 0x41; 1206 if ((int)brightness < 0) 1207 brightness = 0; 1208 if ((int)brightness > 31) 1209 brightness = 31; 1210 pmdata.data[0] = (31 - brightness) * 23 / 10 + 37; 1211 (void)pm_pmgrop_pm2(&pmdata); 1212 break; 1213 case PM_HW_PB1XX: 1214 /* this is an experimental code also */ 1215 pmdata.command = 0x40; 1216 if ((int)brightness < 0) 1217 brightness = 0; 1218 if ((int)brightness > 31) 1219 brightness = 31; 1220 pmdata.data[0] = 31 - brightness; 1221 (void)pm_pmgrop_pm1(&pmdata); 1222 break; 1223 default: 1224 1225 return 0; 1226 break; 1227 } 1228 1229 return brightness; 1230 } 1231