Home | History | Annotate | Line # | Download | only in dev
pm_direct.c revision 1.21.2.1
      1 /*	$NetBSD: pm_direct.c,v 1.21.2.1 2005/04/29 11:28:15 kent Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 1997 Takashi Hamada
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *  This product includes software developed by Takashi Hamada
     18  * 4. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 /* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */
     33 
     34 /*
     35  * TODO : Check bounds on PMData in pmgrop
     36  *		callers should specify how much room for data is in the buffer
     37  *		and that should be respected by the pmgrop
     38  */
     39 
     40 #include <sys/cdefs.h>
     41 __KERNEL_RCSID(0, "$NetBSD: pm_direct.c,v 1.21.2.1 2005/04/29 11:28:15 kent Exp $");
     42 
     43 #ifdef DEBUG
     44 #ifndef ADB_DEBUG
     45 #define ADB_DEBUG
     46 #endif
     47 #endif
     48 
     49 /* #define	PM_GRAB_SI	1 */
     50 
     51 #include <sys/param.h>
     52 #include <sys/cdefs.h>
     53 #include <sys/device.h>
     54 #include <sys/systm.h>
     55 
     56 #include <machine/adbsys.h>
     57 #include <machine/autoconf.h>
     58 #include <machine/cpu.h>
     59 
     60 #include <dev/ofw/openfirm.h>
     61 
     62 #include <macppc/dev/adbvar.h>
     63 #include <macppc/dev/pm_direct.h>
     64 #include <macppc/dev/viareg.h>
     65 
     66 extern int adb_polling;		/* Are we polling?  (Debugger mode) */
     67 
     68 /* hardware dependent values */
     69 #define ADBDelay 100		/* XXX */
     70 
     71 /* useful macros */
     72 #define PM_SR()			read_via_reg(VIA1, vSR)
     73 #define PM_VIA_INTR_ENABLE()	write_via_reg(VIA1, vIER, 0x90)
     74 #define PM_VIA_INTR_DISABLE()	write_via_reg(VIA1, vIER, 0x10)
     75 #define PM_VIA_CLR_INTR()	write_via_reg(VIA1, vIFR, 0x90)
     76 
     77 #define PM_SET_STATE_ACKON()	via_reg_or(VIA2, vBufB, 0x10)
     78 #define PM_SET_STATE_ACKOFF()	via_reg_and(VIA2, vBufB, ~0x10)
     79 #define PM_IS_ON		(0x08 == (read_via_reg(VIA2, vBufB) & 0x08))
     80 #define PM_IS_OFF		(0x00 == (read_via_reg(VIA2, vBufB) & 0x08))
     81 
     82 /*
     83  * Variables for internal use
     84  */
     85 u_short	pm_existent_ADB_devices = 0x0;	/* each bit expresses the existent ADB device */
     86 u_int	pm_LCD_brightness = 0x0;
     87 u_int	pm_LCD_contrast = 0x0;
     88 u_int	pm_counter = 0;			/* clock count */
     89 
     90 static enum batt_type { BATT_COMET, BATT_HOOPER, BATT_SMART } pmu_batt_type;
     91 static int	pmu_nbatt;
     92 static int	strinlist(char *, char *, int);
     93 static enum pmu_type { PMU_UNKNOWN, PMU_OHARE, PMU_G3, PMU_KEYLARGO } pmu_type;
     94 
     95 /* these values shows that number of data returned after 'send' cmd is sent */
     96 signed char pm_send_cmd_type[] = {
     97 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
     98 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
     99 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
    100 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1, 0x00,
    101 	  -1, 0x00, 0x02, 0x01, 0x01,   -1,   -1,   -1,
    102 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    103 	0x04, 0x14,   -1, 0x03,   -1,   -1,   -1,   -1,
    104 	0x00, 0x00, 0x02, 0x02,   -1,   -1,   -1,   -1,
    105 	0x01, 0x01,   -1,   -1,   -1,   -1,   -1,   -1,
    106 	0x00, 0x00,   -1,   -1, 0x01,   -1,   -1,   -1,
    107 	0x01, 0x00, 0x02, 0x02,   -1, 0x01, 0x03, 0x01,
    108 	0x00, 0x01, 0x00, 0x00, 0x00,   -1,   -1,   -1,
    109 	0x02,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    110 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   -1,   -1,
    111 	0x01, 0x01, 0x01,   -1,   -1,   -1,   -1,   -1,
    112 	0x00, 0x00,   -1,   -1,   -1,   -1, 0x04, 0x04,
    113 	0x04,   -1, 0x00,   -1,   -1,   -1,   -1,   -1,
    114 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    115 	0x01, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
    116 	0x00, 0x00,   -1,   -1,   -1,   -1,   -1,   -1,
    117 	0x02, 0x02, 0x02, 0x04,   -1, 0x00,   -1,   -1,
    118 	0x01, 0x01, 0x03, 0x02,   -1,   -1,   -1,   -1,
    119 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    120 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    121 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    122 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    123 	0x00,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    124 	0x01, 0x01,   -1,   -1, 0x00, 0x00,   -1,   -1,
    125 	  -1, 0x04, 0x00,   -1,   -1,   -1,   -1,   -1,
    126 	0x03,   -1, 0x00,   -1, 0x00,   -1,   -1, 0x00,
    127 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    128 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1
    129 };
    130 
    131 /* these values shows that number of data returned after 'receive' cmd is sent */
    132 signed char pm_receive_cmd_type[] = {
    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 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1, 0x00,
    137 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    138 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    139 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    140 	0x05, 0x15,   -1, 0x02,   -1,   -1,   -1,   -1,
    141 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    142 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
    143 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    144 	0x02, 0x00, 0x03, 0x03,   -1,   -1,   -1,   -1,
    145 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    146 	0x04, 0x04, 0x03, 0x09,   -1,   -1,   -1,   -1,
    147 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    148 	  -1,   -1,   -1,   -1,   -1,   -1, 0x01, 0x01,
    149 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    150 	0x06,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    151 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    152 	0x02, 0x02,   -1,   -1,   -1,   -1,   -1,   -1,
    153 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    154 	0x02, 0x00, 0x00, 0x00,   -1,   -1,   -1,   -1,
    155 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    156 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    157 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    158 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    159 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    160 	0x02, 0x02,   -1,   -1, 0x02,   -1,   -1,   -1,
    161 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    162 	  -1,   -1, 0x02,   -1,   -1,   -1,   -1, 0x00,
    163 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    164 	  -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    165 };
    166 
    167 
    168 /*
    169  * Define the private functions
    170  */
    171 
    172 /* for debugging */
    173 #ifdef ADB_DEBUG
    174 void	pm_printerr __P((char *, int, int, char *));
    175 #endif
    176 
    177 int	pm_wait_busy __P((int));
    178 int	pm_wait_free __P((int));
    179 
    180 static int	pm_receive __P((u_char *));
    181 static int	pm_send __P((u_char));
    182 
    183 /* these functions are called from adb_direct.c */
    184 void	pm_setup_adb __P((void));
    185 void	pm_check_adb_devices __P((int));
    186 int	pm_adb_op __P((u_char *, void *, void *, int));
    187 
    188 /* these functions also use the variables of adb_direct.c */
    189 void	pm_adb_get_TALK_result __P((PMData *));
    190 void	pm_adb_get_ADB_data __P((PMData *));
    191 
    192 
    193 /*
    194  * These variables are in adb_direct.c.
    195  */
    196 extern u_char	*adbBuffer;	/* pointer to user data area */
    197 extern void	*adbCompRout;	/* pointer to the completion routine */
    198 extern void	*adbCompData;	/* pointer to the completion routine data */
    199 extern int	adbWaiting;	/* waiting for return data from the device */
    200 extern int	adbWaitingCmd;	/* ADB command we are waiting for */
    201 extern int	adbStarting;	/* doing ADB reinit, so do "polling" differently */
    202 
    203 #define	ADB_MAX_MSG_LENGTH	16
    204 #define	ADB_MAX_HDR_LENGTH	8
    205 struct adbCommand {
    206 	u_char	header[ADB_MAX_HDR_LENGTH];	/* not used yet */
    207 	u_char	data[ADB_MAX_MSG_LENGTH];	/* packet data only */
    208 	u_char	*saveBuf;	/* where to save result */
    209 	u_char	*compRout;	/* completion routine pointer */
    210 	u_char	*compData;	/* completion routine data pointer */
    211 	u_int	cmd;		/* the original command for this data */
    212 	u_int	unsol;		/* 1 if packet was unsolicited */
    213 	u_int	ack_only;	/* 1 for no special processing */
    214 };
    215 extern	void	adb_pass_up __P((struct adbCommand *));
    216 
    217 #if 0
    218 /*
    219  * Define the external functions
    220  */
    221 extern int	zshard __P((int));		/* from zs.c */
    222 #endif
    223 
    224 #ifdef ADB_DEBUG
    225 /*
    226  * This function dumps contents of the PMData
    227  */
    228 void
    229 pm_printerr(ttl, rval, num, data)
    230 	char *ttl;
    231 	int rval;
    232 	int num;
    233 	char *data;
    234 {
    235 	int i;
    236 
    237 	printf("pm: %s:%04x %02x ", ttl, rval, num);
    238 	for (i = 0; i < num; i++)
    239 		printf("%02x ", data[i]);
    240 	printf("\n");
    241 }
    242 #endif
    243 
    244 
    245 
    246 /*
    247  * Check the hardware type of the Power Manager
    248  */
    249 void
    250 pm_setup_adb()
    251 {
    252 }
    253 
    254 /*
    255  * Search for targ in list.  list is an area of listlen bytes
    256  * containing null-terminated strings.
    257  */
    258 static int
    259 strinlist(char *targ, char *list, int listlen)
    260 {
    261 	char	*str;
    262 	int	sl;
    263 	int	targlen;
    264 
    265 	str = list;
    266 	targlen = strlen(targ);
    267 	while (listlen > 0) {
    268 		sl = strlen(str);
    269 		if (sl == targlen && (strncmp(targ, str, sl) == 0))
    270 			return 1;
    271 		str += sl+1;
    272 		listlen -= sl+1;
    273 	}
    274 	return 0;
    275 }
    276 
    277 /*
    278  * Check the hardware type of the Power Manager
    279  */
    280 void
    281 pm_init(void)
    282 {
    283 	uint32_t	regs[10];
    284 	PMData		pmdata;
    285 	char		compat[128];
    286 	int		clen, node, imask;
    287 
    288 	node = OF_peer(0);
    289 	if (node == -1) {
    290 		printf("pmu: Failed to get root");
    291 		return;
    292 	}
    293 	clen = OF_getprop(node, "compatible", compat, sizeof(compat));
    294 	if (clen <= 0) {
    295 		printf("pmu: failed to read root compatible data %d\n", clen);
    296 		return;
    297 	}
    298 
    299 	imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
    300 
    301 	if (strinlist("AAPL,3500", compat, clen) ||
    302 	    strinlist("AAPL,3400/2400", compat, clen)) {
    303 		/* How to distinguish BATT_COMET? */
    304 		pmu_nbatt = 1;
    305 		pmu_batt_type = BATT_HOOPER;
    306 		pmu_type = PMU_OHARE;
    307 	} else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
    308 		   strinlist("PowerBook1,1", compat, clen)) {
    309 		pmu_nbatt = 2;
    310 		pmu_batt_type = BATT_SMART;
    311 		pmu_type = PMU_G3;
    312 	} else {
    313 		pmu_nbatt = 1;
    314 		pmu_batt_type = BATT_SMART;
    315 		pmu_type = PMU_KEYLARGO;
    316 		node = getnodebyname(0, "power-mgt");
    317 		if (node == -1) {
    318 			printf("pmu: can't find power-mgt\n");
    319 			return;
    320 		}
    321 		clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
    322 		if (clen < 24) {
    323 			printf("pmu: failed to read prim-info\n");
    324 			return;
    325 		}
    326 		pmu_nbatt = regs[6] >> 16;
    327 	}
    328 
    329 	pmdata.command = PMU_SET_IMASK;
    330 	pmdata.num_data = 1;
    331 	pmdata.s_buf = pmdata.data;
    332 	pmdata.r_buf = pmdata.data;
    333 	pmdata.data[0] = imask;
    334 	pmgrop(&pmdata);
    335 }
    336 
    337 
    338 /*
    339  * Check the existent ADB devices
    340  */
    341 void
    342 pm_check_adb_devices(id)
    343 	int id;
    344 {
    345 	u_short ed = 0x1;
    346 
    347 	ed <<= id;
    348 	pm_existent_ADB_devices |= ed;
    349 }
    350 
    351 
    352 /*
    353  * Wait until PM IC is busy
    354  */
    355 int
    356 pm_wait_busy(delay)
    357 	int delay;
    358 {
    359 	while (PM_IS_ON) {
    360 #ifdef PM_GRAB_SI
    361 #if 0
    362 		zshard(0);		/* grab any serial interrupts */
    363 #else
    364 		(void)intr_dispatch(0x70);
    365 #endif
    366 #endif
    367 		if ((--delay) < 0)
    368 			return 1;	/* timeout */
    369 	}
    370 	return 0;
    371 }
    372 
    373 
    374 /*
    375  * Wait until PM IC is free
    376  */
    377 int
    378 pm_wait_free(delay)
    379 	int delay;
    380 {
    381 	while (PM_IS_OFF) {
    382 #ifdef PM_GRAB_SI
    383 #if 0
    384 		zshard(0);		/* grab any serial interrupts */
    385 #else
    386 		(void)intr_dispatch(0x70);
    387 #endif
    388 #endif
    389 		if ((--delay) < 0)
    390 			return 0;	/* timeout */
    391 	}
    392 	return 1;
    393 }
    394 
    395 
    396 
    397 /*
    398  * Receive data from PMU
    399  */
    400 static int
    401 pm_receive(data)
    402 	u_char *data;
    403 {
    404 	int i;
    405 	int rval;
    406 
    407 	rval = 0xffffcd34;
    408 
    409 	switch (1) {
    410 	default:
    411 		/* set VIA SR to input mode */
    412 		via_reg_or(VIA1, vACR, 0x0c);
    413 		via_reg_and(VIA1, vACR, ~0x10);
    414 		i = PM_SR();
    415 
    416 		PM_SET_STATE_ACKOFF();
    417 		if (pm_wait_busy((int)ADBDelay*32) != 0)
    418 			break;		/* timeout */
    419 
    420 		PM_SET_STATE_ACKON();
    421 		rval = 0xffffcd33;
    422 		if (pm_wait_free((int)ADBDelay*32) == 0)
    423 			break;		/* timeout */
    424 
    425 		*data = PM_SR();
    426 		rval = 0;
    427 
    428 		break;
    429 	}
    430 
    431 	PM_SET_STATE_ACKON();
    432 	via_reg_or(VIA1, vACR, 0x1c);
    433 
    434 	return rval;
    435 }
    436 
    437 
    438 
    439 /*
    440  * Send data to PMU
    441  */
    442 static int
    443 pm_send(data)
    444 	u_char data;
    445 {
    446 	int rval;
    447 
    448 	via_reg_or(VIA1, vACR, 0x1c);
    449 	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
    450 
    451 	PM_SET_STATE_ACKOFF();
    452 	rval = 0xffffcd36;
    453 	if (pm_wait_busy((int)ADBDelay*32) != 0) {
    454 		PM_SET_STATE_ACKON();
    455 
    456 		via_reg_or(VIA1, vACR, 0x1c);
    457 
    458 		return rval;
    459 	}
    460 
    461 	PM_SET_STATE_ACKON();
    462 	rval = 0xffffcd35;
    463 	if (pm_wait_free((int)ADBDelay*32) != 0)
    464 		rval = 0;
    465 
    466 	PM_SET_STATE_ACKON();
    467 	via_reg_or(VIA1, vACR, 0x1c);
    468 
    469 	return rval;
    470 }
    471 
    472 
    473 
    474 /*
    475  * The PMgrOp routine
    476  */
    477 int
    478 pmgrop(pmdata)
    479 	PMData *pmdata;
    480 {
    481 	int i;
    482 	int s;
    483 	u_char via1_vIER;
    484 	int rval = 0;
    485 	int num_pm_data = 0;
    486 	u_char pm_cmd;
    487 	short pm_num_rx_data;
    488 	u_char pm_data;
    489 	u_char *pm_buf;
    490 
    491 	s = splhigh();
    492 
    493 	/* disable all inetrrupts but PM */
    494 	via1_vIER = 0x10;
    495 	via1_vIER &= read_via_reg(VIA1, vIER);
    496 	write_via_reg(VIA1, vIER, via1_vIER);
    497 	if (via1_vIER != 0x0)
    498 		via1_vIER |= 0x80;
    499 
    500 	switch (pmdata->command) {
    501 	default:
    502 		/* wait until PM is free */
    503 		pm_cmd = (u_char)(pmdata->command & 0xff);
    504 		rval = 0xcd38;
    505 		if (pm_wait_free(ADBDelay * 4) == 0)
    506 			break;			/* timeout */
    507 
    508 		/* send PM command */
    509 		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
    510 			break;				/* timeout */
    511 
    512 		/* send number of PM data */
    513 		num_pm_data = pmdata->num_data;
    514 		if (pm_send_cmd_type[pm_cmd] < 0) {
    515 			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
    516 				break;		/* timeout */
    517 			pmdata->command = 0;
    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(pm_buf[i])) != 0)
    523 				break;			/* timeout */
    524 		if (i != num_pm_data)
    525 			break;				/* timeout */
    526 
    527 
    528 		/* check if PM will send me data  */
    529 		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
    530 		pmdata->num_data = pm_num_rx_data;
    531 		if (pm_num_rx_data == 0) {
    532 			rval = 0;
    533 			break;				/* no return data */
    534 		}
    535 
    536 		/* receive PM command */
    537 		pm_data = pmdata->command;
    538 		pm_num_rx_data--;
    539 		if (pm_num_rx_data == 0)
    540 			if ((rval = pm_receive(&pm_data)) != 0) {
    541 				rval = 0xffffcd37;
    542 				break;
    543 			}
    544 		pmdata->command = pm_data;
    545 
    546 		/* receive number of PM data */
    547 		if (pm_num_rx_data < 0) {
    548 			if ((rval = pm_receive(&pm_data)) != 0)
    549 				break;		/* timeout */
    550 			num_pm_data = pm_data;
    551 		} else
    552 			num_pm_data = pm_num_rx_data;
    553 		pmdata->num_data = num_pm_data;
    554 
    555 		/* receive PM data */
    556 		pm_buf = (u_char *)pmdata->r_buf;
    557 		for (i = 0; i < num_pm_data; i++) {
    558 			if ((rval = pm_receive(&pm_data)) != 0)
    559 				break;			/* timeout */
    560 			pm_buf[i] = pm_data;
    561 		}
    562 
    563 		rval = 0;
    564 	}
    565 
    566 	/* restore former value */
    567 	write_via_reg(VIA1, vIER, via1_vIER);
    568 	splx(s);
    569 
    570 	return rval;
    571 }
    572 
    573 
    574 /*
    575  * My PMU interrupt routine
    576  */
    577 int
    578 pm_intr(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 						/* ask PM what happend */
    588 	pmdata.command = PMU_INT_ACK;
    589 	pmdata.num_data = 0;
    590 	pmdata.s_buf = &pmdata.data[2];
    591 	pmdata.r_buf = &pmdata.data[2];
    592 	rval = pmgrop(&pmdata);
    593 	if (rval != 0) {
    594 #ifdef ADB_DEBUG
    595 		if (adb_debug)
    596 			printf("pm: PM is not ready. error code: %08x\n", rval);
    597 #endif
    598 		splx(s);
    599 		return 0;
    600 	}
    601 
    602 	switch ((u_int)(pmdata.data[2] & 0xff)) {
    603 	case 0x00:		/* no event pending? */
    604 		break;
    605 	case 0x80:		/* 1 sec interrupt? */
    606 		pm_counter++;
    607 		break;
    608 	case 0x08:		/* Brightness/Contrast button on LCD panel */
    609 		/* get brightness and contrast of the LCD */
    610 		pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
    611 		pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
    612 
    613 		/* this is experimental code */
    614 		pmdata.command = PMU_SET_BRIGHTNESS;
    615 		pmdata.num_data = 1;
    616 		pmdata.s_buf = pmdata.data;
    617 		pmdata.r_buf = pmdata.data;
    618 		pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
    619 		if (pm_LCD_brightness < 0x08)
    620 			pm_LCD_brightness = 0x08;
    621 		if (pm_LCD_brightness > 0x78)
    622 			pm_LCD_brightness = 0x78;
    623 		pmdata.data[0] = pm_LCD_brightness;
    624 		rval = pmgrop(&pmdata);
    625 		break;
    626 
    627 	case 0x10:		/* ADB data requested by TALK command */
    628 	case 0x14:
    629 		pm_adb_get_TALK_result(&pmdata);
    630 		break;
    631 	case 0x16:		/* ADB device event */
    632 	case 0x18:
    633 	case 0x1e:
    634 		pm_adb_get_ADB_data(&pmdata);
    635 		break;
    636 	default:
    637 #ifdef ADB_DEBUG
    638 		if (adb_debug)
    639 			pm_printerr("driver does not support this event.",
    640 			    pmdata.data[2], pmdata.num_data,
    641 			    pmdata.data);
    642 #endif
    643 		break;
    644 	}
    645 
    646 	splx(s);
    647 
    648 	return 1;
    649 }
    650 
    651 
    652 /*
    653  * Synchronous ADBOp routine for the Power Manager
    654  */
    655 int
    656 pm_adb_op(buffer, compRout, data, command)
    657 	u_char *buffer;
    658 	void *compRout;
    659 	void *data;
    660 	int command;
    661 {
    662 	int i;
    663 	int s;
    664 	int rval;
    665 	int timo;
    666 	PMData pmdata;
    667 	struct adbCommand packet;
    668 
    669 	if (adbWaiting == 1)
    670 		return 1;
    671 
    672 	s = splhigh();
    673 	write_via_reg(VIA1, vIER, 0x10);
    674 
    675  	adbBuffer = buffer;
    676 	adbCompRout = compRout;
    677 	adbCompData = data;
    678 
    679 	pmdata.command = PMU_ADB_CMD;
    680 	pmdata.s_buf = pmdata.data;
    681 	pmdata.r_buf = pmdata.data;
    682 
    683 	/* if the command is LISTEN, add number of ADB data to number of PM data */
    684 	if ((command & 0xc) == 0x8) {
    685 		if (buffer != (u_char *)0)
    686 			pmdata.num_data = buffer[0] + 3;
    687 	} else {
    688 		pmdata.num_data = 3;
    689 	}
    690 
    691 	pmdata.data[0] = (u_char)(command & 0xff);
    692 	pmdata.data[1] = 0;
    693 	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
    694 		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
    695 			pmdata.data[2] = buffer[0];		/* number of data */
    696 			for (i = 0; i < buffer[0]; i++)
    697 				pmdata.data[3 + i] = buffer[1 + i];
    698 		} else
    699 			pmdata.data[2] = 0;
    700 	} else
    701 		pmdata.data[2] = 0;
    702 
    703 	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
    704 		/* set up stuff for adb_pass_up */
    705 		packet.data[0] = 1 + pmdata.data[2];
    706 		packet.data[1] = command;
    707 		for (i = 0; i < pmdata.data[2]; i++)
    708 			packet.data[i+2] = pmdata.data[i+3];
    709 		packet.saveBuf = adbBuffer;
    710 		packet.compRout = adbCompRout;
    711 		packet.compData = adbCompData;
    712 		packet.cmd = command;
    713 		packet.unsol = 0;
    714 		packet.ack_only = 1;
    715 		adb_polling = 1;
    716 		adb_pass_up(&packet);
    717 		adb_polling = 0;
    718 	}
    719 
    720 	rval = pmgrop(&pmdata);
    721 	if (rval != 0) {
    722 		splx(s);
    723 		return 1;
    724 	}
    725 
    726 	delay(10000);
    727 
    728 	adbWaiting = 1;
    729 	adbWaitingCmd = command;
    730 
    731 	PM_VIA_INTR_ENABLE();
    732 
    733 	/* wait until the PM interrupt has occurred */
    734 	timo = 0x80000;
    735 	while (adbWaiting == 1) {
    736 		if (read_via_reg(VIA1, vIFR) & 0x14)
    737 			pm_intr(NULL);
    738 #ifdef PM_GRAB_SI
    739 #if 0
    740 			zshard(0);		/* grab any serial interrupts */
    741 #else
    742 			(void)intr_dispatch(0x70);
    743 #endif
    744 #endif
    745 		if ((--timo) < 0) {
    746 			/* Try to take an interrupt anyway, just in case.
    747 			 * This has been observed to happen on my ibook
    748 			 * when i press a key after boot and before adb
    749 			 * is attached;  For example, when booting with -d.
    750 			 */
    751 			pm_intr(NULL);
    752 			if (adbWaiting) {
    753 				printf("pm_adb_op: timeout. command = 0x%x\n",command);
    754 				splx(s);
    755 				return 1;
    756 			}
    757 #ifdef ADB_DEBUG
    758 			else {
    759 				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
    760 			}
    761 #endif
    762 		}
    763 	}
    764 
    765 	/* this command enables the interrupt by operating ADB devices */
    766 	pmdata.command = PMU_ADB_CMD;
    767 	pmdata.num_data = 4;
    768 	pmdata.s_buf = pmdata.data;
    769 	pmdata.r_buf = pmdata.data;
    770 	pmdata.data[0] = 0x00;
    771 	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
    772 	pmdata.data[2] = 0x00;
    773 	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
    774 	rval = pmgrop(&pmdata);
    775 
    776 	splx(s);
    777 	return rval;
    778 }
    779 
    780 
    781 void
    782 pm_adb_get_TALK_result(pmdata)
    783 	PMData *pmdata;
    784 {
    785 	int i;
    786 	struct adbCommand packet;
    787 
    788 	/* set up data for adb_pass_up */
    789 	packet.data[0] = pmdata->num_data-1;
    790 	packet.data[1] = pmdata->data[3];
    791 	for (i = 0; i <packet.data[0]-1; i++)
    792 		packet.data[i+2] = pmdata->data[i+4];
    793 
    794 	packet.saveBuf = adbBuffer;
    795 	packet.compRout = adbCompRout;
    796 	packet.compData = adbCompData;
    797 	packet.unsol = 0;
    798 	packet.ack_only = 0;
    799 	adb_polling = 1;
    800 	adb_pass_up(&packet);
    801 	adb_polling = 0;
    802 
    803 	adbWaiting = 0;
    804 	adbBuffer = (long)0;
    805 	adbCompRout = (long)0;
    806 	adbCompData = (long)0;
    807 }
    808 
    809 
    810 void
    811 pm_adb_get_ADB_data(pmdata)
    812 	PMData *pmdata;
    813 {
    814 	int i;
    815 	struct adbCommand packet;
    816 
    817 	if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
    818 	    pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
    819 	    ((pmdata->data[2] & ~1) == 0xf4)) {
    820 		if (pmdata->data[2] == 0xf4) {
    821 			pm_eject_pcmcia(0);
    822 		} else {
    823 			pm_eject_pcmcia(1);
    824 		}
    825 		return;
    826 	}
    827 	/* set up data for adb_pass_up */
    828 	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
    829 	packet.data[1] = pmdata->data[3];	/* ADB command */
    830 	for (i = 0; i <packet.data[0]-1; i++)
    831 		packet.data[i+2] = pmdata->data[i+4];
    832 	packet.unsol = 1;
    833 	packet.ack_only = 0;
    834 	adb_pass_up(&packet);
    835 }
    836 
    837 
    838 void
    839 pm_adb_restart()
    840 {
    841 	PMData p;
    842 
    843 	p.command = PMU_RESET_CPU;
    844 	p.num_data = 0;
    845 	p.s_buf = p.data;
    846 	p.r_buf = p.data;
    847 	pmgrop(&p);
    848 }
    849 
    850 void
    851 pm_adb_poweroff()
    852 {
    853 	PMData p;
    854 
    855 	p.command = PMU_POWER_OFF;
    856 	p.num_data = 4;
    857 	p.s_buf = p.data;
    858 	p.r_buf = p.data;
    859 	strcpy(p.data, "MATT");
    860 	pmgrop(&p);
    861 }
    862 
    863 void
    864 pm_read_date_time(time)
    865 	u_long *time;
    866 {
    867 	PMData p;
    868 
    869 	p.command = PMU_READ_RTC;
    870 	p.num_data = 0;
    871 	p.s_buf = p.data;
    872 	p.r_buf = p.data;
    873 	pmgrop(&p);
    874 
    875 	memcpy(time, p.data, 4);
    876 }
    877 
    878 void
    879 pm_set_date_time(time)
    880 	u_long time;
    881 {
    882 	PMData p;
    883 
    884 	p.command = PMU_SET_RTC;
    885 	p.num_data = 4;
    886 	p.s_buf = p.r_buf = p.data;
    887 	memcpy(p.data, &time, 4);
    888 	pmgrop(&p);
    889 }
    890 
    891 int
    892 pm_read_brightness()
    893 {
    894 	PMData p;
    895 
    896 	p.command = PMU_READ_BRIGHTNESS;
    897 	p.num_data = 1;		/* XXX why 1? */
    898 	p.s_buf = p.r_buf = p.data;
    899 	p.data[0] = 0;
    900 	pmgrop(&p);
    901 
    902 	return p.data[0];
    903 }
    904 
    905 void
    906 pm_set_brightness(val)
    907 	int val;
    908 {
    909 	PMData p;
    910 
    911 	val = 0x7f - val / 2;
    912 	if (val < 0x08)
    913 		val = 0x08;
    914 	if (val > 0x78)
    915 		val = 0x78;
    916 
    917 	p.command = PMU_SET_BRIGHTNESS;
    918 	p.num_data = 1;
    919 	p.s_buf = p.r_buf = p.data;
    920 	p.data[0] = val;
    921 	pmgrop(&p);
    922 }
    923 
    924 void
    925 pm_init_brightness()
    926 {
    927 	int val;
    928 
    929 	val = pm_read_brightness();
    930 	pm_set_brightness(val);
    931 }
    932 
    933 void
    934 pm_eject_pcmcia(slot)
    935 	int slot;
    936 {
    937 	PMData p;
    938 
    939 	if (slot != 0 && slot != 1)
    940 		return;
    941 
    942 	p.command = PMU_EJECT_PCMCIA;
    943 	p.num_data = 1;
    944 	p.s_buf = p.r_buf = p.data;
    945 	p.data[0] = 5 + slot;	/* XXX */
    946 	pmgrop(&p);
    947 }
    948 
    949 /*
    950  * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
    951  * for a clear description of the PMU results.
    952  */
    953 static int
    954 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
    955 {
    956 	PMData p;
    957 
    958 	p.command = PMU_SMART_BATTERY_STATE;
    959 	p.num_data = 1;
    960 	p.s_buf = p.r_buf = p.data;
    961 	p.data[0] = battery + 1;
    962 	pmgrop(&p);
    963 
    964 	info->flags = p.data[1];
    965 
    966 	info->secs_remaining = 0;
    967 	switch (p.data[0]) {
    968 	case 3:
    969 	case 4:
    970 		info->cur_charge = p.data[2];
    971 		info->max_charge = p.data[3];
    972 		info->draw = *((signed char *)&p.data[4]);
    973 		info->voltage = p.data[5];
    974 		break;
    975 	case 5:
    976 		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
    977 		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
    978 		info->draw = *((signed short *)&p.data[6]);
    979 		info->voltage = ((p.data[8] << 8) | (p.data[7]));
    980 		break;
    981 	default:
    982 		/* XXX - Error condition */
    983 		info->cur_charge = 0;
    984 		info->max_charge = 0;
    985 		info->draw = 0;
    986 		info->voltage = 0;
    987 		break;
    988 	}
    989 	if (info->draw) {
    990 		if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
    991 			info->secs_remaining =
    992 				((info->max_charge - info->cur_charge) * 3600)
    993 				/ info->draw;
    994 		} else {
    995 			info->secs_remaining =
    996 				(info->cur_charge * 3600) / -info->draw;
    997 		}
    998 	}
    999 
   1000 	return 1;
   1001 }
   1002 
   1003 static int
   1004 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
   1005 {
   1006 	PMData p;
   1007 	long pcharge=0, charge, vb, vmax, lmax;
   1008 	long vmax_charging, vmax_charged, amperage, voltage;
   1009 
   1010 	p.command = PMU_BATTERY_STATE;
   1011 	p.num_data = 0;
   1012 	p.s_buf = p.r_buf = p.data;
   1013 	pmgrop(&p);
   1014 
   1015 	info->flags = p.data[0];
   1016 
   1017 	if (info->flags & PMU_PWR_BATT_PRESENT) {
   1018 		if (ty == BATT_COMET) {
   1019 			vmax_charging = 213;
   1020 			vmax_charged = 189;
   1021 			lmax = 6500;
   1022 		} else {
   1023 			/* Experimental values */
   1024 			vmax_charging = 365;
   1025 			vmax_charged = 365;
   1026 			lmax = 6500;
   1027 		}
   1028 		vmax = vmax_charged;
   1029 		vb = (p.data[1] << 8) | p.data[2];
   1030 		voltage = (vb * 256 + 72665) / 10;
   1031 		amperage = (unsigned char) p.data[5];
   1032 		if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
   1033 			if (amperage > 200)
   1034 				vb += ((amperage - 200) * 15)/100;
   1035 		} else if (info->flags & PMU_PWR_BATT_CHARGING) {
   1036 			vb = (vb * 97) / 100;
   1037 			vmax = vmax_charging;
   1038 		}
   1039 		charge = (100 * vb) / vmax;
   1040 		if (info->flags & PMU_PWR_PCHARGE_RESET) {
   1041 			pcharge = (p.data[6] << 8) | p.data[7];
   1042 			if (pcharge > lmax)
   1043 				pcharge = lmax;
   1044 			pcharge *= 100;
   1045 			pcharge = 100 - pcharge / lmax;
   1046 			if (pcharge < charge)
   1047 				charge = pcharge;
   1048 		}
   1049 		info->cur_charge = charge;
   1050 		info->max_charge = 100;
   1051 		info->draw = -amperage;
   1052 		info->voltage = voltage;
   1053 		if (amperage > 0)
   1054 			info->secs_remaining = (charge * 16440) / amperage;
   1055 		else
   1056 			info->secs_remaining = 0;
   1057 	} else {
   1058 		info->cur_charge = 0;
   1059 		info->max_charge = 0;
   1060 		info->draw = 0;
   1061 		info->voltage = 0;
   1062 		info->secs_remaining = 0;
   1063 	}
   1064 
   1065 	return 1;
   1066 }
   1067 
   1068 int
   1069 pm_battery_info(int battery, struct pmu_battery_info *info)
   1070 {
   1071 
   1072 	if (battery > pmu_nbatt)
   1073 		return 0;
   1074 
   1075 	switch (pmu_batt_type) {
   1076 	case BATT_COMET:
   1077 	case BATT_HOOPER:
   1078 		return pm_battery_info_legacy(battery, info, pmu_batt_type);
   1079 
   1080 	case BATT_SMART:
   1081 		return pm_battery_info_smart(battery, info);
   1082 	}
   1083 
   1084 	return 0;
   1085 }
   1086 
   1087 int
   1088 pm_read_nvram(addr)
   1089 	int addr;
   1090 {
   1091 	PMData p;
   1092 
   1093 	p.command = PMU_READ_NVRAM;
   1094 	p.num_data = 2;
   1095 	p.s_buf = p.r_buf = p.data;
   1096 	p.data[0] = addr >> 8;
   1097 	p.data[1] = addr;
   1098 	pmgrop(&p);
   1099 
   1100 	return p.data[0];
   1101 }
   1102 
   1103 void
   1104 pm_write_nvram(addr, val)
   1105 	int addr, val;
   1106 {
   1107 	PMData p;
   1108 
   1109 	p.command = PMU_WRITE_NVRAM;
   1110 	p.num_data = 3;
   1111 	p.s_buf = p.r_buf = p.data;
   1112 	p.data[0] = addr >> 8;
   1113 	p.data[1] = addr;
   1114 	p.data[2] = val;
   1115 	pmgrop(&p);
   1116 }
   1117