1 /* $NetBSD: pm_direct.c,v 1.43 2025/05/14 06:31:45 nat Exp $ */ 2 3 /* 4 * Copyright (c) 2024, 2025 Nathanial Sloss <nathanialsloss (at) 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.43 2025/05/14 06:31: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/kthread.h> 52 #include <sys/proc.h> 53 #include <sys/mutex.h> 54 #include <sys/sysctl.h> 55 #include <sys/systm.h> 56 57 #include <dev/sysmon/sysmonvar.h> 58 59 #include <machine/viareg.h> 60 #include <machine/param.h> 61 #include <machine/cpu.h> 62 #include <machine/adbsys.h> 63 64 #include <mac68k/mac68k/macrom.h> 65 #include <mac68k/dev/adbvar.h> 66 #include <mac68k/dev/pm_direct.h> 67 68 /* hardware dependent values */ 69 extern u_short ADBDelay; 70 extern u_int32_t HwCfgFlags3; 71 extern struct mac68k_machine_S mac68k_machine; 72 73 74 /* useful macros */ 75 #define PM_SR() via_reg(VIA1, vSR) 76 #define PM_VIA_INTR_ENABLE() via_reg(VIA1, vIER) = 0x90 77 #define PM_VIA_INTR_DISABLE() via_reg(VIA1, vIER) = 0x10 78 #define PM_VIA_CLR_INTR() via_reg(VIA1, vIFR) = 0x90 79 #define PM_SET_STATE_ACKON() via_reg(VIA2, vBufB) |= 0x04 80 #define PM_SET_STATE_ACKOFF() via_reg(VIA2, vBufB) &= ~0x04 81 #define PM_IS_ON (0x02 == (via_reg(VIA2, vBufB) & 0x02)) 82 #define PM_IS_OFF (0x00 == (via_reg(VIA2, vBufB) & 0x02)) 83 84 /* 85 * Variables for internal use 86 */ 87 int pmHardware = PM_HW_UNKNOWN; 88 u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */ 89 u_int pm_LCD_brightness = 0x0; 90 u_int pm_LCD_contrast = 0x0; 91 u_int pm_counter = 0; /* clock count */ 92 struct sysmon_pswitch pbutton; 93 struct sysctllog *sc_log; /* Sysctl log */ 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 /* Spin mutex to seriaize powermanager requests. */ 169 kmutex_t pm_mutex; 170 171 /* 172 * Define the private functions 173 */ 174 175 /* for debugging */ 176 #ifdef ADB_DEBUG 177 void pm_printerr(const char *, int, int, char *); 178 #endif 179 180 int pm_wait_busy(int); 181 int pm_wait_free(int); 182 183 /* these functions are for the PB1XX series */ 184 int pm_receive_pm1(u_char *); 185 int pm_send_pm1(u_char, int); 186 int pm_pmgrop_pm1(PMData *); 187 void pm_intr_pm1(void *); 188 void brightness_slider(void *); /* brightness slider thread */ 189 190 /* these functions are for the PB Duo series and the PB 5XX series */ 191 int pm_receive_pm2(u_char *); 192 int pm_send_pm2(u_char); 193 int pm_pmgrop_pm2(PMData *); 194 void pm_intr_pm2(void *); 195 196 /* this function is MRG-Based (for testing) */ 197 int pm_pmgrop_mrg(PMData *); 198 199 /* these functions are called from adb_direct.c */ 200 void pm_setup_adb(void); 201 void pm_check_adb_devices(int); 202 void pm_intr(void *); 203 int pm_adb_op(u_char *, void *, void *, int); 204 void pm_hw_setup(void); 205 206 /* these functions also use the variables of adb_direct.c */ 207 void pm_adb_get_TALK_result(PMData *); 208 void pm_adb_get_ADB_data(PMData *); 209 void pm_adb_poll_next_device_pm1(PMData *); 210 211 static void brightness_sysctl_setup(void *); 212 static int sysctl_brightness(SYSCTLFN_PROTO); 213 214 /* 215 * These variables are in adb_direct.c. 216 */ 217 extern u_char *adbBuffer; /* pointer to user data area */ 218 extern void *adbCompRout; /* pointer to the completion routine */ 219 extern void *adbCompData; /* pointer to the completion routine data */ 220 extern int adbWaiting; /* waiting for return data from the device */ 221 extern int adbWaitingCmd; /* ADB command we are waiting for */ 222 extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ 223 224 #define ADB_MAX_MSG_LENGTH 16 225 #define ADB_MAX_HDR_LENGTH 8 226 struct adbCommand { 227 u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ 228 u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ 229 u_char *saveBuf; /* where to save result */ 230 u_char *compRout; /* completion routine pointer */ 231 u_char *compData; /* completion routine data pointer */ 232 u_int cmd; /* the original command for this data */ 233 u_int unsol; /* 1 if packet was unsolicited */ 234 u_int ack_only; /* 1 for no special processing */ 235 }; 236 extern void adb_pass_up(struct adbCommand *); 237 238 #ifdef ADB_DEBUG 239 /* 240 * This function dumps contents of the PMData 241 */ 242 void 243 pm_printerr(const char *ttl, int rval, int num, char *data) 244 { 245 int i; 246 247 printf("pm: %s:%04x %02x ", ttl, rval, num); 248 for (i = 0; i < num; i++) 249 printf("%02x ", data[i]); 250 printf("\n"); 251 } 252 #endif 253 254 255 256 /* 257 * Check the hardware type of the Power Manager 258 */ 259 void 260 pm_setup_adb(void) 261 { 262 mutex_init(&pm_mutex, MUTEX_DEFAULT, IPL_HIGH); 263 switch (mac68k_machine.machineid) { 264 case MACH_MACPB140: 265 case MACH_MACPB145: 266 case MACH_MACPB160: 267 case MACH_MACPB165: 268 case MACH_MACPB165C: 269 case MACH_MACPB170: 270 case MACH_MACPB180: 271 case MACH_MACPB180C: 272 pmHardware = PM_HW_PB1XX; 273 274 memset(&pbutton, 0, sizeof(struct sysmon_pswitch)); 275 pbutton.smpsw_name = "PB"; 276 pbutton.smpsw_type = PSWITCH_TYPE_POWER; 277 if (sysmon_pswitch_register(&pbutton) != 0) 278 printf("Unable to register soft power\n"); 279 brightness_sysctl_setup(NULL); 280 kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, 281 brightness_slider, NULL, NULL, "britethrd"); 282 break; 283 case MACH_MACPB150: 284 case MACH_MACPB210: 285 case MACH_MACPB230: 286 case MACH_MACPB250: 287 case MACH_MACPB270: 288 case MACH_MACPB280: 289 case MACH_MACPB280C: 290 case MACH_MACPB500: 291 case MACH_MACPB190: 292 case MACH_MACPB190CS: 293 pmHardware = PM_HW_PB5XX; 294 #if notyet 295 memset(&pbutton, 0, sizeof(struct sysmon_pswitch)); 296 pbutton.smpsw_name = "PB"; 297 pbutton.smpsw_type = PSWITCH_TYPE_POWER; 298 if (sysmon_pswitch_register(&pbutton) != 0) 299 printf("Unable to register soft power\n"); 300 #endif 301 break; 302 default: 303 break; 304 } 305 } 306 307 308 /* 309 * Check the existent ADB devices 310 */ 311 void 312 pm_check_adb_devices(int id) 313 { 314 u_short ed = 0x1; 315 316 ed <<= id; 317 pm_existent_ADB_devices |= ed; 318 } 319 320 321 /* 322 * Wait until PM IC is busy 323 */ 324 int 325 pm_wait_busy(int xdelay) 326 { 327 while (PM_IS_ON) { 328 #ifdef PM_GRAB_SI 329 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 330 #endif 331 if ((--xdelay) < 0) 332 return 1; /* timeout */ 333 } 334 return 0; 335 } 336 337 338 /* 339 * Wait until PM IC is free 340 */ 341 int 342 pm_wait_free(int xdelay) 343 { 344 while (PM_IS_OFF) { 345 #ifdef PM_GRAB_SI 346 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 347 #endif 348 if ((--xdelay) < 0) 349 return 0; /* timeout */ 350 } 351 return 1; 352 } 353 354 355 356 /* 357 * Functions for the PB1XX series 358 */ 359 360 /* 361 * Receive data from PM for the PB1XX series 362 */ 363 int 364 pm_receive_pm1(u_char *data) 365 { 366 int rval = 0xffffcd34; 367 368 via_reg(VIA2, vDirA) = 0x00; 369 370 switch (1) { 371 default: 372 if (pm_wait_busy(0x40) != 0) 373 break; /* timeout */ 374 375 PM_SET_STATE_ACKOFF(); 376 *data = via_reg(VIA2, 0x200); 377 378 rval = 0xffffcd33; 379 if (pm_wait_free(0x40) == 0) 380 break; /* timeout */ 381 382 rval = 0x00; 383 break; 384 } 385 386 PM_SET_STATE_ACKON(); 387 via_reg(VIA2, vDirA) = 0x00; 388 389 return rval; 390 } 391 392 393 394 /* 395 * Send data to PM for the PB1XX series 396 */ 397 int 398 pm_send_pm1(u_char data, int timo) 399 { 400 int rval; 401 402 via_reg(VIA2, vDirA) = 0xff; 403 via_reg(VIA2, 0x200) = data; 404 405 PM_SET_STATE_ACKOFF(); 406 #if 0 407 if (pm_wait_busy(0x400) == 0) { 408 #else 409 if (pm_wait_busy(timo) == 0) { 410 #endif 411 PM_SET_STATE_ACKON(); 412 if (pm_wait_free(0x40) != 0) 413 rval = 0x0; 414 else 415 rval = 0xffffcd35; 416 } else { 417 rval = 0xffffcd36; 418 } 419 420 PM_SET_STATE_ACKON(); 421 via_reg(VIA2, vDirA) = 0x00; 422 423 return rval; 424 } 425 426 427 /* 428 * My PMgrOp routine for the PB1XX series 429 */ 430 int 431 pm_pmgrop_pm1(PMData *pmdata) 432 { 433 int i; 434 int s = 0x81815963; 435 u_char via1_vIER, via1_vDirA; 436 int rval = 0; 437 int num_pm_data = 0; 438 u_char pm_cmd; 439 u_char pm_data; 440 u_char *pm_buf; 441 442 mutex_spin_enter(&pm_mutex); 443 444 /* disable all interrupts but PM */ 445 via1_vIER = via_reg(VIA1, vIER); 446 PM_VIA_INTR_DISABLE(); 447 448 via1_vDirA = via_reg(VIA1, vDirA); 449 450 switch (pmdata->command) { 451 default: 452 for (i = 0; i < 7; i++) { 453 via_reg(VIA2, vDirA) = 0x00; 454 455 /* wait until PM is free */ 456 if (pm_wait_free(ADBDelay) == 0) { /* timeout */ 457 via_reg(VIA2, vDirA) = 0x00; 458 /* restore formar value */ 459 via_reg(VIA1, vDirA) = via1_vDirA; 460 via_reg(VIA1, vIER) = via1_vIER; 461 mutex_spin_exit(&pm_mutex); 462 return 0xffffcd38; 463 } 464 465 switch (mac68k_machine.machineid) { 466 case MACH_MACPB160: 467 case MACH_MACPB165: 468 case MACH_MACPB165C: 469 case MACH_MACPB170: 470 case MACH_MACPB180: 471 case MACH_MACPB180C: 472 { 473 int xdelay = ADBDelay * 16; 474 475 via_reg(VIA2, vDirA) = 0x00; 476 while ((via_reg(VIA2, 0x200) == 0x7f) && (xdelay >= 0)) 477 xdelay--; 478 479 if (xdelay < 0) { /* timeout */ 480 via_reg(VIA2, vDirA) = 0x00; 481 /* restore formar value */ 482 via_reg(VIA1, vIER) = via1_vIER; 483 mutex_spin_exit(&pm_mutex); 484 return 0xffffcd38; 485 } 486 } 487 } /* end switch */ 488 489 s = splhigh(); 490 491 via1_vDirA = via_reg(VIA1, vDirA); 492 via_reg(VIA1, vDirA) &= 0x7f; 493 494 pm_cmd = (u_char)(pmdata->command & 0xff); 495 if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0) 496 break; /* send command succeeded */ 497 498 via_reg(VIA1, vDirA) = via1_vDirA; 499 splx(s); 500 } /* end for */ 501 502 /* failed to send a command */ 503 if (i == 7) { 504 via_reg(VIA2, vDirA) = 0x00; 505 /* restore formar value */ 506 via_reg(VIA1, vDirA) = via1_vDirA; 507 via_reg(VIA1, vIER) = via1_vIER; 508 if (s != 0x81815963) 509 splx(s); 510 mutex_spin_exit(&pm_mutex); 511 return 0xffffcd38; 512 } 513 514 /* send # of PM data */ 515 num_pm_data = pmdata->num_data; 516 if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0) 517 break; /* timeout */ 518 519 /* send PM data */ 520 pm_buf = (u_char *)pmdata->s_buf; 521 for (i = 0; i < num_pm_data; i++) 522 if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0) 523 break; /* timeout */ 524 if ((i != num_pm_data) && (num_pm_data != 0)) 525 break; /* timeout */ 526 527 /* Will PM IC return data? */ 528 if ((pm_cmd & 0x08) == 0) { 529 rval = 0; 530 break; /* no returned data */ 531 } 532 533 rval = 0xffffcd37; 534 if (pm_wait_busy(ADBDelay) != 0) 535 break; /* timeout */ 536 537 /* receive PM command */ 538 if ((rval = pm_receive_pm1(&pm_data)) != 0) 539 break; 540 541 pmdata->command = pm_data; 542 543 /* receive number of PM data */ 544 if ((rval = pm_receive_pm1(&pm_data)) != 0) 545 break; /* timeout */ 546 num_pm_data = pm_data; 547 pmdata->num_data = num_pm_data; 548 549 /* receive PM data */ 550 pm_buf = (u_char *)pmdata->r_buf; 551 for (i = 0; i < num_pm_data; i++) { 552 if ((rval = pm_receive_pm1(&pm_data)) != 0) 553 break; /* timeout */ 554 pm_buf[i] = pm_data; 555 } 556 557 rval = 0; 558 } 559 560 via_reg(VIA2, vDirA) = 0x00; 561 562 /* restore formar value */ 563 via_reg(VIA1, vDirA) = via1_vDirA; 564 via_reg(VIA1, vIER) = via1_vIER; 565 if (s != 0x81815963) 566 splx(s); 567 568 mutex_spin_exit(&pm_mutex); 569 570 return rval; 571 } 572 573 574 /* 575 * My PM interrupt routine for PB1XX series 576 */ 577 void 578 pm_intr_pm1(void *arg) 579 { 580 int s; 581 int rval; 582 PMData pmdata; 583 584 s = splhigh(); 585 586 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 587 588 /* ask PM what happened */ 589 pmdata.command = 0x78; 590 pmdata.num_data = 0; 591 pmdata.data[0] = pmdata.data[1] = 0; 592 pmdata.s_buf = &pmdata.data[2]; 593 pmdata.r_buf = &pmdata.data[2]; 594 rval = pm_pmgrop_pm1(&pmdata); 595 if (rval != 0) { 596 #ifdef ADB_DEBUG 597 if (adb_debug) 598 printf("pm: PM is not ready. error code=%08x\n", rval); 599 #endif 600 splx(s); 601 return; 602 } 603 604 if ((pmdata.data[2] & 0x10) == 0x10) { 605 if ((pmdata.data[2] & 0x0f) == 0) { 606 /* ADB data that were requested by TALK command */ 607 pm_adb_get_TALK_result(&pmdata); 608 } else if ((pmdata.data[2] & 0x08) == 0x8) { 609 /* PM is requesting to poll */ 610 pm_adb_poll_next_device_pm1(&pmdata); 611 } else if ((pmdata.data[2] & 0x04) == 0x4) { 612 /* ADB device event */ 613 pm_adb_get_ADB_data(&pmdata); 614 } 615 } else if ((pmdata.num_data == 0x1) && (pmdata.data[0] == 0)) { 616 /* PowerBook 160/180 Power button. */ 617 sysmon_pswitch_event(&pbutton, PSWITCH_EVENT_PRESSED); 618 } else { 619 #ifdef ADB_DEBUG 620 if (adb_debug) 621 pm_printerr("driver does not supported this event.", 622 rval, pmdata.num_data, pmdata.data); 623 #endif 624 } 625 626 splx(s); 627 } 628 629 void 630 brightness_slider(void *arg) 631 { 632 int s; 633 int rval; 634 PMData pmdata; 635 636 for (;;) { 637 kpause("brslider", false, hz / 4, NULL); 638 639 s = splhigh(); 640 641 pmdata.command = 0x49; 642 pmdata.num_data = 0; 643 pmdata.data[0] = pmdata.data[1] = 0; 644 pmdata.s_buf = &pmdata.data[0]; 645 pmdata.r_buf = &pmdata.data[0]; 646 rval = pm_pmgrop_pm1(&pmdata); 647 if (rval != 0) { 648 #ifdef ADB_DEBUG 649 if (adb_debug) { 650 printf("pm: PM is not ready. " 651 "error code=%08x\n", rval); 652 } 653 #endif 654 splx(s); 655 continue; 656 } 657 658 if (((uint8_t)pmdata.data[0] / 8) != pm_LCD_brightness) { 659 pm_LCD_brightness = (uint8_t)pmdata.data[0] / 8; 660 pm_LCD_brightness = 661 pm_set_brightness(pm_LCD_brightness); 662 } 663 664 splx(s); 665 } 666 } 667 668 /* 669 * Functions for the PB Duo series and the PB 5XX series 670 */ 671 672 /* 673 * Receive data from PM for the PB Duo series and the PB 5XX series 674 */ 675 int 676 pm_receive_pm2(u_char *data) 677 { 678 int rval; 679 680 rval = 0xffffcd34; 681 682 switch (1) { 683 default: 684 /* set VIA SR to input mode */ 685 via_reg(VIA1, vACR) |= 0x0c; 686 via_reg(VIA1, vACR) &= ~0x10; 687 PM_SR(); 688 689 PM_SET_STATE_ACKOFF(); 690 if (pm_wait_busy((int)ADBDelay*32) != 0) 691 break; /* timeout */ 692 693 PM_SET_STATE_ACKON(); 694 rval = 0xffffcd33; 695 if (pm_wait_free((int)ADBDelay*32) == 0) 696 break; /* timeout */ 697 698 *data = PM_SR(); 699 rval = 0; 700 701 break; 702 } 703 704 PM_SET_STATE_ACKON(); 705 via_reg(VIA1, vACR) |= 0x1c; 706 707 return rval; 708 } 709 710 711 712 /* 713 * Send data to PM for the PB Duo series and the PB 5XX series 714 */ 715 int 716 pm_send_pm2(u_char data) 717 { 718 int rval; 719 720 via_reg(VIA1, vACR) |= 0x1c; 721 PM_SR() = data; 722 723 PM_SET_STATE_ACKOFF(); 724 if (pm_wait_busy((int)ADBDelay*32) == 0) { 725 PM_SET_STATE_ACKON(); 726 if (pm_wait_free((int)ADBDelay*32) != 0) 727 rval = 0; 728 else 729 rval = 0xffffcd35; 730 } else { 731 rval = 0xffffcd36; 732 } 733 734 PM_SET_STATE_ACKON(); 735 via_reg(VIA1, vACR) |= 0x1c; 736 737 return rval; 738 } 739 740 741 742 /* 743 * My PMgrOp routine for the PB Duo series and the PB 5XX series 744 */ 745 int 746 pm_pmgrop_pm2(PMData *pmdata) 747 { 748 int i; 749 int s; 750 u_char via1_vIER; 751 int rval = 0; 752 int num_pm_data = 0; 753 u_char pm_cmd; 754 short pm_num_rx_data; 755 u_char pm_data; 756 u_char *pm_buf; 757 758 mutex_spin_enter(&pm_mutex); 759 s = splhigh(); 760 761 /* disable all interrupts but PM */ 762 via1_vIER = 0x10; 763 via1_vIER &= via_reg(VIA1, vIER); 764 via_reg(VIA1, vIER) = via1_vIER; 765 if (via1_vIER != 0x0) 766 via1_vIER |= 0x80; 767 768 switch (pmdata->command) { 769 default: 770 /* wait until PM is free */ 771 pm_cmd = (u_char)(pmdata->command & 0xff); 772 rval = 0xcd38; 773 if (pm_wait_free(ADBDelay * 4) == 0) 774 break; /* timeout */ 775 776 if (HwCfgFlags3 & 0x00200000) { 777 /* PB 160, PB 165(c), PB 180(c)? */ 778 int xdelay = ADBDelay * 16; 779 780 via_reg(VIA2, vDirA) = 0x00; 781 while ((via_reg(VIA2, 0x200) == 0x07) && 782 (xdelay >= 0)) 783 xdelay--; 784 785 if (xdelay < 0) { 786 rval = 0xffffcd38; 787 break; /* timeout */ 788 } 789 } 790 791 /* send PM command */ 792 if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff)))) 793 break; /* timeout */ 794 795 /* send number of PM data */ 796 num_pm_data = pmdata->num_data; 797 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 798 if (pm_send_cmd_type[pm_cmd] < 0) { 799 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 800 break; /* timeout */ 801 pmdata->command = 0; 802 } 803 } else { /* PB 1XX series ? */ 804 if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) 805 break; /* timeout */ 806 } 807 /* send PM data */ 808 pm_buf = (u_char *)pmdata->s_buf; 809 for (i = 0 ; i < num_pm_data; i++) 810 if ((rval = pm_send_pm2(pm_buf[i])) != 0) 811 break; /* timeout */ 812 if (i != num_pm_data) 813 break; /* timeout */ 814 815 816 /* check if PM will send me data */ 817 pm_num_rx_data = pm_receive_cmd_type[pm_cmd]; 818 pmdata->num_data = pm_num_rx_data; 819 if (pm_num_rx_data == 0) { 820 rval = 0; 821 break; /* no return data */ 822 } 823 824 /* receive PM command */ 825 pm_data = pmdata->command; 826 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 827 pm_num_rx_data--; 828 if (pm_num_rx_data == 0) 829 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 830 rval = 0xffffcd37; 831 break; 832 } 833 pmdata->command = pm_data; 834 } else { /* PB 1XX series ? */ 835 if ((rval = pm_receive_pm2(&pm_data)) != 0) { 836 rval = 0xffffcd37; 837 break; 838 } 839 pmdata->command = pm_data; 840 } 841 842 /* receive number of PM data */ 843 if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ 844 if (pm_num_rx_data < 0) { 845 if ((rval = pm_receive_pm2(&pm_data)) != 0) 846 break; /* timeout */ 847 num_pm_data = pm_data; 848 } else 849 num_pm_data = pm_num_rx_data; 850 pmdata->num_data = num_pm_data; 851 } else { /* PB 1XX serias ? */ 852 if ((rval = pm_receive_pm2(&pm_data)) != 0) 853 break; /* timeout */ 854 num_pm_data = pm_data; 855 pmdata->num_data = num_pm_data; 856 } 857 858 /* receive PM data */ 859 pm_buf = (u_char *)pmdata->r_buf; 860 for (i = 0; i < num_pm_data; i++) { 861 if ((rval = pm_receive_pm2(&pm_data)) != 0) 862 break; /* timeout */ 863 pm_buf[i] = pm_data; 864 } 865 866 rval = 0; 867 } 868 869 /* restore former value */ 870 via_reg(VIA1, vIER) = via1_vIER; 871 splx(s); 872 873 mutex_spin_exit(&pm_mutex); 874 return rval; 875 } 876 877 878 /* 879 * My PM interrupt routine for the PB Duo series and the PB 5XX series 880 */ 881 void 882 pm_intr_pm2(void *arg) 883 { 884 int s; 885 int rval; 886 PMData pmdata; 887 888 s = splhigh(); 889 890 PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ 891 /* ask PM what happened */ 892 pmdata.command = 0x78; 893 pmdata.num_data = 0; 894 pmdata.s_buf = &pmdata.data[2]; 895 pmdata.r_buf = &pmdata.data[2]; 896 rval = pm_pmgrop_pm2(&pmdata); 897 if (rval != 0) { 898 #ifdef ADB_DEBUG 899 if (adb_debug) 900 printf("pm: PM is not ready. error code: %08x\n", rval); 901 #endif 902 splx(s); 903 return; 904 } 905 906 switch ((u_int)(pmdata.data[2] & 0xff)) { 907 case 0x00: /* 1 sec interrupt? */ 908 break; 909 case 0x80: /* 1 sec interrupt? */ 910 pm_counter++; 911 break; 912 case 0x08: /* Brightness/Contrast button on LCD panel */ 913 /* get brightness and contrast of the LCD */ 914 pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff; 915 pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff; 916 /* 917 pm_printerr("#08", rval, pmdata.num_data, pmdata.data); 918 pmdata.command = 0x33; 919 pmdata.num_data = 1; 920 pmdata.s_buf = pmdata.data; 921 pmdata.r_buf = pmdata.data; 922 pmdata.data[0] = pm_LCD_contrast; 923 rval = pm_pmgrop_pm2(&pmdata); 924 pm_printerr("#33", rval, pmdata.num_data, pmdata.data); 925 */ 926 pm_LCD_brightness = 927 pm_set_brightness(pm_LCD_brightness); 928 break; 929 case 0x10: /* ADB data that were requested by TALK command */ 930 case 0x14: 931 pm_adb_get_TALK_result(&pmdata); 932 break; 933 case 0x16: /* ADB device event */ 934 case 0x18: 935 case 0x1e: 936 pm_adb_get_ADB_data(&pmdata); 937 break; 938 default: 939 #ifdef ADB_DEBUG 940 if (adb_debug) 941 pm_printerr("driver does not supported this event.", 942 pmdata.data[2], pmdata.num_data, 943 pmdata.data); 944 #endif 945 break; 946 } 947 948 splx(s); 949 } 950 951 952 /* 953 * MRG-based PMgrOp routine 954 */ 955 int 956 pm_pmgrop_mrg(PMData *pmdata) 957 { 958 u_int32_t rval=0; 959 960 __asm volatile( 961 " movl %1,%%a0 \n" 962 " .word 0xa085 \n" 963 " movl %%d0,%0" 964 : "=g" (rval) 965 : "g" (pmdata) 966 : "a0","d0"); 967 968 return rval; 969 } 970 971 972 /* 973 * My PMgrOp routine 974 */ 975 int 976 pmgrop(PMData *pmdata) 977 { 978 switch (pmHardware) { 979 case PM_HW_PB1XX: 980 return (pm_pmgrop_pm1(pmdata)); 981 break; 982 case PM_HW_PB5XX: 983 return (pm_pmgrop_pm2(pmdata)); 984 break; 985 default: 986 /* return (pmgrop_mrg(pmdata)); */ 987 return 1; 988 } 989 } 990 991 992 /* 993 * My PM interrupt routine 994 */ 995 void 996 pm_intr(void *arg) 997 { 998 switch (pmHardware) { 999 case PM_HW_PB1XX: 1000 pm_intr_pm1(arg); 1001 break; 1002 case PM_HW_PB5XX: 1003 pm_intr_pm2(arg); 1004 break; 1005 default: 1006 break; 1007 } 1008 } 1009 1010 1011 void 1012 pm_hw_setup(void) 1013 { 1014 switch (pmHardware) { 1015 case PM_HW_PB1XX: 1016 via1_register_irq(4, pm_intr_pm1, (void *)0); 1017 PM_VIA_CLR_INTR(); 1018 break; 1019 case PM_HW_PB5XX: 1020 via1_register_irq(4, pm_intr_pm2, (void *)0); 1021 PM_VIA_CLR_INTR(); 1022 break; 1023 default: 1024 break; 1025 } 1026 } 1027 1028 1029 /* 1030 * Synchronous ADBOp routine for the Power Manager 1031 */ 1032 int 1033 pm_adb_op(u_char *buffer, void *compRout, void *data, int command) 1034 { 1035 int i; 1036 int s; 1037 int rval; 1038 int xdelay; 1039 PMData pmdata; 1040 struct adbCommand packet; 1041 1042 if (adbWaiting == 1) 1043 return 1; 1044 1045 s = splhigh(); 1046 via_reg(VIA1, vIER) = 0x10; 1047 1048 adbBuffer = buffer; 1049 adbCompRout = compRout; 1050 adbCompData = data; 1051 1052 pmdata.command = 0x20; 1053 pmdata.s_buf = pmdata.data; 1054 pmdata.r_buf = pmdata.data; 1055 1056 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, add number of ADB data to number of PM data */ 1057 if (buffer != (u_char *)0) 1058 pmdata.num_data = buffer[0] + 3; 1059 } else { 1060 pmdata.num_data = 3; 1061 } 1062 1063 pmdata.data[0] = (u_char)(command & 0xff); 1064 pmdata.data[1] = 0; 1065 if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ 1066 if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { 1067 pmdata.data[2] = buffer[0]; /* number of data */ 1068 for (i = 0; i < buffer[0]; i++) 1069 pmdata.data[3 + i] = buffer[1 + i]; 1070 } else 1071 pmdata.data[2] = 0; 1072 } else 1073 pmdata.data[2] = 0; 1074 1075 if ((command & 0xc) != 0xc) { /* if the command is not TALK */ 1076 /* set up stuff fNULLor adb_pass_up */ 1077 packet.data[0] = 1 + pmdata.data[2]; 1078 packet.data[1] = command; 1079 for (i = 0; i < pmdata.data[2]; i++) 1080 packet.data[i+2] = pmdata.data[i+3]; 1081 packet.saveBuf = adbBuffer; 1082 packet.compRout = adbCompRout; 1083 packet.compData = adbCompData; 1084 packet.cmd = command; 1085 packet.unsol = 0; 1086 packet.ack_only = 1; 1087 adb_polling = 1; 1088 adb_pass_up(&packet); 1089 adb_polling = 0; 1090 } 1091 1092 rval = pmgrop(&pmdata); 1093 if (rval != 0) { 1094 splx(s); 1095 return 1; 1096 } 1097 1098 adbWaiting = 1; 1099 adbWaitingCmd = command; 1100 1101 PM_VIA_INTR_ENABLE(); 1102 1103 /* wait until the PM interrupt has occurred */ 1104 xdelay = 0x80000; 1105 while (adbWaiting == 1) { 1106 switch (mac68k_machine.machineid) { 1107 case MACH_MACPB150: 1108 case MACH_MACPB210: 1109 case MACH_MACPB230: /* daishi tested with Duo230 */ 1110 case MACH_MACPB250: 1111 case MACH_MACPB270: 1112 case MACH_MACPB280: 1113 case MACH_MACPB280C: 1114 case MACH_MACPB190: 1115 case MACH_MACPB190CS: 1116 pm_intr((void *)0); 1117 break; 1118 default: 1119 if ((via_reg(VIA1, vIFR) & 0x10) == 0x10) 1120 pm_intr((void *)0); 1121 break; 1122 } 1123 #ifdef PM_GRAB_SI 1124 (void)intr_dispatch(0x70); /* grab any serial interrupts */ 1125 #endif 1126 if ((--xdelay) < 0) { 1127 splx(s); 1128 return 1; 1129 } 1130 } 1131 1132 /* this command enables the interrupt by operating ADB devices */ 1133 if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */ 1134 pmdata.command = 0x20; 1135 pmdata.num_data = 4; 1136 pmdata.s_buf = pmdata.data; 1137 pmdata.r_buf = pmdata.data; 1138 pmdata.data[0] = 0x00; 1139 pmdata.data[1] = 0x86; /* magic spell for awaking the PM */ 1140 pmdata.data[2] = 0x00; 1141 pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */ 1142 } else { /* PB 1XX series */ 1143 pmdata.command = 0x20; 1144 pmdata.num_data = 3; 1145 pmdata.s_buf = pmdata.data; 1146 pmdata.r_buf = pmdata.data; 1147 pmdata.data[0] = (u_char)(command & 0xf0) | 0xc; 1148 pmdata.data[1] = 0x04; 1149 pmdata.data[2] = 0x00; 1150 } 1151 rval = pmgrop(&pmdata); 1152 1153 splx(s); 1154 return rval; 1155 } 1156 1157 1158 void 1159 pm_adb_get_TALK_result(PMData *pmdata) 1160 { 1161 int i; 1162 struct adbCommand packet; 1163 1164 /* set up data for adb_pass_up */ 1165 packet.data[0] = pmdata->num_data-1; 1166 packet.data[1] = pmdata->data[3]; 1167 for (i = 0; i <packet.data[0]-1; i++) 1168 packet.data[i+2] = pmdata->data[i+4]; 1169 1170 packet.saveBuf = adbBuffer; 1171 packet.compRout = adbCompRout; 1172 packet.compData = adbCompData; 1173 packet.unsol = 0; 1174 packet.ack_only = 0; 1175 adb_polling = 1; 1176 adb_pass_up(&packet); 1177 adb_polling = 0; 1178 1179 adbWaiting = 0; 1180 adbBuffer = (long)0; 1181 adbCompRout = (long)0; 1182 adbCompData = (long)0; 1183 } 1184 1185 1186 void 1187 pm_adb_get_ADB_data(PMData *pmdata) 1188 { 1189 int i; 1190 struct adbCommand packet; 1191 1192 /* set up data for adb_pass_up */ 1193 packet.data[0] = pmdata->num_data-1; /* number of raw data */ 1194 packet.data[1] = pmdata->data[3]; /* ADB command */ 1195 for (i = 0; i <packet.data[0]-1; i++) 1196 packet.data[i+2] = pmdata->data[i+4]; 1197 packet.unsol = 1; 1198 packet.ack_only = 0; 1199 adb_pass_up(&packet); 1200 } 1201 1202 1203 void 1204 pm_adb_poll_next_device_pm1(PMData *pmdata) 1205 { 1206 int i; 1207 int ndid; 1208 u_short bendid = 0x1; 1209 PMData tmp_pmdata; 1210 1211 /* find another existent ADB device to poll */ 1212 for (i = 1; i < 16; i++) { 1213 ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf; 1214 bendid <<= ndid; 1215 if ((pm_existent_ADB_devices & bendid) != 0) 1216 break; 1217 } 1218 1219 /* poll the other device */ 1220 tmp_pmdata.command = 0x20; 1221 tmp_pmdata.num_data = 3; 1222 tmp_pmdata.s_buf = tmp_pmdata.data; 1223 tmp_pmdata.r_buf = tmp_pmdata.data; 1224 tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; 1225 tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ 1226 tmp_pmdata.data[2] = 0x00; 1227 pmgrop(&tmp_pmdata); 1228 } 1229 1230 void 1231 pm_poweroff(void) 1232 { 1233 PMData pmdata; 1234 int attempt = 3; 1235 1236 while (pmHardware == PM_HW_PB1XX && attempt > 0) { 1237 pmdata.command = 0xef; 1238 pmdata.num_data = 0; 1239 pmdata.data[0] = pmdata.data[1] = 0; 1240 pmdata.s_buf = &pmdata.data[2]; 1241 pmdata.r_buf = &pmdata.data[2]; 1242 (void)pm_pmgrop_pm1(&pmdata); 1243 attempt--; 1244 } 1245 1246 return; 1247 } 1248 1249 u_int 1250 pm_set_brightness(u_int brightness) 1251 { 1252 PMData pmdata; 1253 1254 pmdata.num_data = 1; 1255 pmdata.s_buf = pmdata.data; 1256 pmdata.r_buf = pmdata.data; 1257 1258 switch (pmHardware) { 1259 case PM_HW_PB5XX: 1260 /* this is an experimental code */ 1261 pmdata.command = 0x41; 1262 if ((int)brightness < 0) 1263 brightness = 0; 1264 if ((int)brightness > 31) 1265 brightness = 31; 1266 pmdata.data[0] = (31 - brightness) * 23 / 10 + 37; 1267 (void)pm_pmgrop_pm2(&pmdata); 1268 break; 1269 case PM_HW_PB1XX: 1270 /* this is an experimental code also */ 1271 pmdata.command = 0x40; 1272 if ((int)brightness < 0) 1273 brightness = 0; 1274 if ((int)brightness > 31) 1275 brightness = 31; 1276 pmdata.data[0] = 31 - brightness; 1277 (void)pm_pmgrop_pm1(&pmdata); 1278 break; 1279 default: 1280 1281 return 0; 1282 break; 1283 } 1284 1285 return brightness; 1286 } 1287 1288 static void 1289 brightness_sysctl_setup(void *arg) 1290 { 1291 const struct sysctlnode *rnode; 1292 1293 if ((sysctl_createv(&sc_log, 0, NULL, &rnode, 1294 0, CTLTYPE_NODE, "screen", 1295 SYSCTL_DESCR("Internal display output device controls"), 1296 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) 1297 goto fail; 1298 1299 (void)sysctl_createv(&sc_log, 0, &rnode, NULL, 1300 CTLFLAG_READWRITE, CTLTYPE_INT, "brightness", 1301 SYSCTL_DESCR("Current brightness level"), 1302 sysctl_brightness, 0, NULL, 0, CTL_CREATE, CTL_EOL); 1303 1304 return; 1305 1306 fail: 1307 aprint_error("screen: couldn't add sysctl nodes\n"); 1308 } 1309 1310 static int 1311 sysctl_brightness(SYSCTLFN_ARGS) 1312 { 1313 struct sysctlnode node; 1314 int val, error; 1315 1316 node = *rnode; 1317 1318 val = pm_LCD_brightness; 1319 1320 node.sysctl_data = &val; 1321 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1322 if (error || newp == NULL) 1323 return error; 1324 1325 val = pm_set_brightness(val); 1326 1327 return error; 1328 } 1329