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