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