Home | History | Annotate | Line # | Download | only in dev
pm_direct.c revision 1.32
      1 /*	$NetBSD: pm_direct.c,v 1.32 2007/10/17 19:55:19 garbled 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.32 2007/10/17 19:55:19 garbled 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/device.h>
     53 #include <sys/systm.h>
     54 
     55 #include <machine/adbsys.h>
     56 #include <machine/autoconf.h>
     57 #include <machine/cpu.h>
     58 #include <machine/pio.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(const 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((const char *, int, int, const 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 *, adbComp *, volatile int *, 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 adbComp	*adbCompRout;	/* pointer to the completion routine */
    198 extern volatile int *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 	adbComp	*compRout;	/* completion routine pointer */
    210 	volatile int	*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 	const char *ttl;
    231 	int rval;
    232 	int num;
    233 	const 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(const 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, pm_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 	pm_imask =
    300 	    PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK;
    301 
    302 	if (strinlist("AAPL,3500", compat, clen) ||
    303 	    strinlist("AAPL,3400/2400", compat, clen)) {
    304 		/* How to distinguish BATT_COMET? */
    305 		pmu_nbatt = 1;
    306 		pmu_batt_type = BATT_HOOPER;
    307 		pmu_type = PMU_OHARE;
    308 	} else if (strinlist("AAPL,PowerBook1998", compat, clen) ||
    309 		   strinlist("PowerBook1,1", compat, clen)) {
    310 		pmu_nbatt = 2;
    311 		pmu_batt_type = BATT_SMART;
    312 		pmu_type = PMU_G3;
    313 	} else {
    314 		pmu_nbatt = 1;
    315 		pmu_batt_type = BATT_SMART;
    316 		pmu_type = PMU_KEYLARGO;
    317 		node = getnodebyname(0, "power-mgt");
    318 		if (node == -1) {
    319 			printf("pmu: can't find power-mgt\n");
    320 			return;
    321 		}
    322 		clen = OF_getprop(node, "prim-info", regs, sizeof(regs));
    323 		if (clen < 24) {
    324 			printf("pmu: failed to read prim-info\n");
    325 			return;
    326 		}
    327 		pmu_nbatt = regs[6] >> 16;
    328 	}
    329 
    330 	pmdata.command = PMU_SET_IMASK;
    331 	pmdata.num_data = 1;
    332 	pmdata.s_buf = pmdata.data;
    333 	pmdata.r_buf = pmdata.data;
    334 	pmdata.data[0] = pm_imask;
    335 	pmgrop(&pmdata);
    336 }
    337 
    338 
    339 /*
    340  * Check the existent ADB devices
    341  */
    342 void
    343 pm_check_adb_devices(id)
    344 	int id;
    345 {
    346 	u_short ed = 0x1;
    347 
    348 	ed <<= id;
    349 	pm_existent_ADB_devices |= ed;
    350 }
    351 
    352 
    353 /*
    354  * Wait until PM IC is busy
    355  */
    356 int
    357 pm_wait_busy(delaycycles)
    358 	int delaycycles;
    359 {
    360 	while (PM_IS_ON) {
    361 #ifdef PM_GRAB_SI
    362 #if 0
    363 		zshard(0);		/* grab any serial interrupts */
    364 #else
    365 		(void)intr_dispatch(0x70);
    366 #endif
    367 #endif
    368 		if ((--delaycycles) < 0)
    369 			return 1;	/* timeout */
    370 	}
    371 	return 0;
    372 }
    373 
    374 
    375 /*
    376  * Wait until PM IC is free
    377  */
    378 int
    379 pm_wait_free(delaycycles)
    380 	int delaycycles;
    381 {
    382 	while (PM_IS_OFF) {
    383 #ifdef PM_GRAB_SI
    384 #if 0
    385 		zshard(0);		/* grab any serial interrupts */
    386 #else
    387 		(void)intr_dispatch(0x70);
    388 #endif
    389 #endif
    390 		if ((--delaycycles) < 0)
    391 			return 0;	/* timeout */
    392 	}
    393 	return 1;
    394 }
    395 
    396 
    397 
    398 /*
    399  * Receive data from PMU
    400  */
    401 static int
    402 pm_receive(data)
    403 	u_char *data;
    404 {
    405 	int i;
    406 	int rval;
    407 
    408 	rval = 0xffffcd34;
    409 
    410 	switch (1) {
    411 	default:
    412 		/* set VIA SR to input mode */
    413 		via_reg_or(VIA1, vACR, 0x0c);
    414 		via_reg_and(VIA1, vACR, ~0x10);
    415 		i = PM_SR();
    416 
    417 		PM_SET_STATE_ACKOFF();
    418 		if (pm_wait_busy((int)ADBDelay*32) != 0)
    419 			break;		/* timeout */
    420 
    421 		PM_SET_STATE_ACKON();
    422 		rval = 0xffffcd33;
    423 		if (pm_wait_free((int)ADBDelay*32) == 0)
    424 			break;		/* timeout */
    425 
    426 		*data = PM_SR();
    427 		rval = 0;
    428 
    429 		break;
    430 	}
    431 
    432 	PM_SET_STATE_ACKON();
    433 	via_reg_or(VIA1, vACR, 0x1c);
    434 
    435 	return rval;
    436 }
    437 
    438 
    439 
    440 /*
    441  * Send data to PMU
    442  */
    443 static int
    444 pm_send(data)
    445 	u_char data;
    446 {
    447 	int rval;
    448 
    449 	via_reg_or(VIA1, vACR, 0x1c);
    450 	write_via_reg(VIA1, vSR, data);	/* PM_SR() = data; */
    451 
    452 	PM_SET_STATE_ACKOFF();
    453 	rval = 0xffffcd36;
    454 	if (pm_wait_busy((int)ADBDelay*32) != 0) {
    455 		PM_SET_STATE_ACKON();
    456 
    457 		via_reg_or(VIA1, vACR, 0x1c);
    458 
    459 		return rval;
    460 	}
    461 
    462 	PM_SET_STATE_ACKON();
    463 	rval = 0xffffcd35;
    464 	if (pm_wait_free((int)ADBDelay*32) != 0)
    465 		rval = 0;
    466 
    467 	PM_SET_STATE_ACKON();
    468 	via_reg_or(VIA1, vACR, 0x1c);
    469 
    470 	return rval;
    471 }
    472 
    473 
    474 
    475 /*
    476  * The PMgrOp routine
    477  */
    478 int
    479 pmgrop(pmdata)
    480 	PMData *pmdata;
    481 {
    482 	int i;
    483 	int s;
    484 	u_char via1_vIER;
    485 	int rval = 0;
    486 	int num_pm_data = 0;
    487 	u_char pm_cmd;
    488 	short pm_num_rx_data;
    489 	u_char pm_data;
    490 	u_char *pm_buf;
    491 
    492 	s = splhigh();
    493 
    494 	/* disable all inetrrupts but PM */
    495 	via1_vIER = 0x10;
    496 	via1_vIER &= read_via_reg(VIA1, vIER);
    497 	write_via_reg(VIA1, vIER, via1_vIER);
    498 	if (via1_vIER != 0x0)
    499 		via1_vIER |= 0x80;
    500 
    501 	switch (pmdata->command) {
    502 	default:
    503 		/* wait until PM is free */
    504 		pm_cmd = (u_char)(pmdata->command & 0xff);
    505 		rval = 0xcd38;
    506 		if (pm_wait_free(ADBDelay * 4) == 0)
    507 			break;			/* timeout */
    508 
    509 		/* send PM command */
    510 		if ((rval = pm_send((u_char)(pm_cmd & 0xff))))
    511 			break;				/* timeout */
    512 
    513 		/* send number of PM data */
    514 		num_pm_data = pmdata->num_data;
    515 		if (pm_send_cmd_type[pm_cmd] < 0) {
    516 			if ((rval = pm_send((u_char)(num_pm_data & 0xff))) != 0)
    517 				break;		/* timeout */
    518 			pmdata->command = 0;
    519 		}
    520 		/* send PM data */
    521 		pm_buf = (u_char *)pmdata->s_buf;
    522 		for (i = 0 ; i < num_pm_data; i++)
    523 			if ((rval = pm_send(pm_buf[i])) != 0)
    524 				break;			/* timeout */
    525 		if (i != num_pm_data)
    526 			break;				/* timeout */
    527 
    528 
    529 		/* check if PM will send me data  */
    530 		pm_num_rx_data = pm_receive_cmd_type[pm_cmd];
    531 		pmdata->num_data = pm_num_rx_data;
    532 		if (pm_num_rx_data == 0) {
    533 			rval = 0;
    534 			break;				/* no return data */
    535 		}
    536 
    537 		/* receive PM command */
    538 		pm_data = pmdata->command;
    539 		pm_num_rx_data--;
    540 		if (pm_num_rx_data == 0)
    541 			if ((rval = pm_receive(&pm_data)) != 0) {
    542 				rval = 0xffffcd37;
    543 				break;
    544 			}
    545 		pmdata->command = pm_data;
    546 
    547 		/* receive number of PM data */
    548 		if (pm_num_rx_data < 0) {
    549 			if ((rval = pm_receive(&pm_data)) != 0)
    550 				break;		/* timeout */
    551 			num_pm_data = pm_data;
    552 		} else
    553 			num_pm_data = pm_num_rx_data;
    554 		pmdata->num_data = num_pm_data;
    555 
    556 		/* receive PM data */
    557 		pm_buf = (u_char *)pmdata->r_buf;
    558 		for (i = 0; i < num_pm_data; i++) {
    559 			if ((rval = pm_receive(&pm_data)) != 0)
    560 				break;			/* timeout */
    561 			pm_buf[i] = pm_data;
    562 		}
    563 
    564 		rval = 0;
    565 	}
    566 
    567 	/* restore former value */
    568 	write_via_reg(VIA1, vIER, via1_vIER);
    569 	splx(s);
    570 
    571 	return rval;
    572 }
    573 
    574 
    575 /*
    576  * My PMU interrupt routine
    577  */
    578 int
    579 pm_intr(void *arg)
    580 {
    581 	int s;
    582 	int rval;
    583 	PMData pmdata;
    584 
    585 	s = splhigh();
    586 
    587 	PM_VIA_CLR_INTR();			/* clear VIA1 interrupt */
    588 						/* ask PM what happend */
    589 	pmdata.command = PMU_INT_ACK;
    590 	pmdata.num_data = 0;
    591 	pmdata.s_buf = &pmdata.data[2];
    592 	pmdata.r_buf = &pmdata.data[2];
    593 	rval = pmgrop(&pmdata);
    594 	if (rval != 0) {
    595 #ifdef ADB_DEBUG
    596 		if (adb_debug)
    597 			printf("pm: PM is not ready. error code: %08x\n", rval);
    598 #endif
    599 		splx(s);
    600 		return 0;
    601 	}
    602 
    603 	switch ((u_int)(pmdata.data[2] & 0xff)) {
    604 	case 0x00:		/* no event pending? */
    605 		break;
    606 	case 0x80:		/* 1 sec interrupt? */
    607 		pm_counter++;
    608 		break;
    609 	case 0x08:		/* Brightness/Contrast button on LCD panel */
    610 		/* get brightness and contrast of the LCD */
    611 		pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff;
    612 		pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff;
    613 
    614 		/* this is experimental code */
    615 		pmdata.command = PMU_SET_BRIGHTNESS;
    616 		pmdata.num_data = 1;
    617 		pmdata.s_buf = pmdata.data;
    618 		pmdata.r_buf = pmdata.data;
    619 		pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2;
    620 		if (pm_LCD_brightness < 0x08)
    621 			pm_LCD_brightness = 0x08;
    622 		if (pm_LCD_brightness > 0x78)
    623 			pm_LCD_brightness = 0x78;
    624 		pmdata.data[0] = pm_LCD_brightness;
    625 		rval = pmgrop(&pmdata);
    626 		break;
    627 
    628 	case 0x10:		/* ADB data requested by TALK command */
    629 	case 0x14:
    630 		pm_adb_get_TALK_result(&pmdata);
    631 		break;
    632 	case 0x16:		/* ADB device event */
    633 	case 0x18:
    634 	case 0x1e:
    635 		pm_adb_get_ADB_data(&pmdata);
    636 		break;
    637 	default:
    638 #ifdef ADB_DEBUG
    639 		if (adb_debug)
    640 			pm_printerr("driver does not support this event.",
    641 			    pmdata.data[2], pmdata.num_data,
    642 			    pmdata.data);
    643 #endif
    644 		break;
    645 	}
    646 
    647 	splx(s);
    648 
    649 	return 1;
    650 }
    651 
    652 
    653 /*
    654  * Synchronous ADBOp routine for the Power Manager
    655  */
    656 int
    657 pm_adb_op(buffer, compRout, data, command)
    658 	u_char *buffer;
    659 	adbComp *compRout;
    660 	volatile int *data;
    661 	int command;
    662 {
    663 	int i;
    664 	int s;
    665 	int rval;
    666 	int timo;
    667 	PMData pmdata;
    668 	struct adbCommand packet;
    669 
    670 	if (adbWaiting == 1)
    671 		return 1;
    672 
    673 	s = splhigh();
    674 	write_via_reg(VIA1, vIER, 0x10);
    675 
    676  	adbBuffer = buffer;
    677 	adbCompRout = compRout;
    678 	adbCompData = data;
    679 
    680 	pmdata.command = PMU_ADB_CMD;
    681 	pmdata.s_buf = pmdata.data;
    682 	pmdata.r_buf = pmdata.data;
    683 
    684 	/* if the command is LISTEN, add number of ADB data to number of PM data */
    685 	if ((command & 0xc) == 0x8) {
    686 		if (buffer != (u_char *)0)
    687 			pmdata.num_data = buffer[0] + 3;
    688 	} else {
    689 		pmdata.num_data = 3;
    690 	}
    691 
    692 	pmdata.data[0] = (u_char)(command & 0xff);
    693 	pmdata.data[1] = 0;
    694 	if ((command & 0xc) == 0x8) {		/* if the command is LISTEN, copy ADB data to PM buffer */
    695 		if ((buffer != (u_char *)0) && (buffer[0] <= 24)) {
    696 			pmdata.data[2] = buffer[0];		/* number of data */
    697 			for (i = 0; i < buffer[0]; i++)
    698 				pmdata.data[3 + i] = buffer[1 + i];
    699 		} else
    700 			pmdata.data[2] = 0;
    701 	} else
    702 		pmdata.data[2] = 0;
    703 
    704 	if ((command & 0xc) != 0xc) {		/* if the command is not TALK */
    705 		/* set up stuff for adb_pass_up */
    706 		packet.data[0] = 1 + pmdata.data[2];
    707 		packet.data[1] = command;
    708 		for (i = 0; i < pmdata.data[2]; i++)
    709 			packet.data[i+2] = pmdata.data[i+3];
    710 		packet.saveBuf = adbBuffer;
    711 		packet.compRout = adbCompRout;
    712 		packet.compData = adbCompData;
    713 		packet.cmd = command;
    714 		packet.unsol = 0;
    715 		packet.ack_only = 1;
    716 		adb_polling = 1;
    717 		adb_pass_up(&packet);
    718 		adb_polling = 0;
    719 	}
    720 
    721 	rval = pmgrop(&pmdata);
    722 	if (rval != 0) {
    723 		splx(s);
    724 		return 1;
    725 	}
    726 
    727 	delay(10000);
    728 
    729 	adbWaiting = 1;
    730 	adbWaitingCmd = command;
    731 
    732 	PM_VIA_INTR_ENABLE();
    733 
    734 	/* wait until the PM interrupt has occurred */
    735 	timo = 0x80000;
    736 	while (adbWaiting == 1) {
    737 		if (read_via_reg(VIA1, vIFR) & 0x14)
    738 			pm_intr(NULL);
    739 #ifdef PM_GRAB_SI
    740 #if 0
    741 			zshard(0);		/* grab any serial interrupts */
    742 #else
    743 			(void)intr_dispatch(0x70);
    744 #endif
    745 #endif
    746 		if ((--timo) < 0) {
    747 			/* Try to take an interrupt anyway, just in case.
    748 			 * This has been observed to happen on my ibook
    749 			 * when i press a key after boot and before adb
    750 			 * is attached;  For example, when booting with -d.
    751 			 */
    752 			pm_intr(NULL);
    753 			if (adbWaiting) {
    754 				printf("pm_adb_op: timeout. command = 0x%x\n",command);
    755 				splx(s);
    756 				return 1;
    757 			}
    758 #ifdef ADB_DEBUG
    759 			else {
    760 				printf("pm_adb_op: missed interrupt. cmd=0x%x\n",command);
    761 			}
    762 #endif
    763 		}
    764 	}
    765 
    766 	/* this command enables the interrupt by operating ADB devices */
    767 	pmdata.command = PMU_ADB_CMD;
    768 	pmdata.num_data = 4;
    769 	pmdata.s_buf = pmdata.data;
    770 	pmdata.r_buf = pmdata.data;
    771 	pmdata.data[0] = 0x00;
    772 	pmdata.data[1] = 0x86;	/* magic spell for awaking the PM */
    773 	pmdata.data[2] = 0x00;
    774 	pmdata.data[3] = 0x0c;	/* each bit may express the existent ADB device */
    775 	rval = pmgrop(&pmdata);
    776 
    777 	splx(s);
    778 	return rval;
    779 }
    780 
    781 
    782 void
    783 pm_adb_get_TALK_result(pmdata)
    784 	PMData *pmdata;
    785 {
    786 	int i;
    787 	struct adbCommand packet;
    788 
    789 	/* set up data for adb_pass_up */
    790 	packet.data[0] = pmdata->num_data-1;
    791 	packet.data[1] = pmdata->data[3];
    792 	for (i = 0; i <packet.data[0]-1; i++)
    793 		packet.data[i+2] = pmdata->data[i+4];
    794 
    795 	packet.saveBuf = adbBuffer;
    796 	packet.compRout = adbCompRout;
    797 	packet.compData = adbCompData;
    798 	packet.unsol = 0;
    799 	packet.ack_only = 0;
    800 	adb_polling = 1;
    801 	adb_pass_up(&packet);
    802 	adb_polling = 0;
    803 
    804 	adbWaiting = 0;
    805 	adbBuffer = (long)0;
    806 	adbCompRout = (long)0;
    807 	adbCompData = (long)0;
    808 }
    809 
    810 
    811 void
    812 pm_adb_get_ADB_data(pmdata)
    813 	PMData *pmdata;
    814 {
    815 	int i;
    816 	struct adbCommand packet;
    817 
    818 	if (pmu_type == PMU_OHARE && pmdata->num_data == 4 &&
    819 	    pmdata->data[1] == 0x2c && pmdata->data[3] == 0xff &&
    820 	    ((pmdata->data[2] & ~1) == 0xf4)) {
    821 		if (pmdata->data[2] == 0xf4) {
    822 			pm_eject_pcmcia(0);
    823 		} else {
    824 			pm_eject_pcmcia(1);
    825 		}
    826 		return;
    827 	}
    828 	/* set up data for adb_pass_up */
    829 	packet.data[0] = pmdata->num_data-1;	/* number of raw data */
    830 	packet.data[1] = pmdata->data[3];	/* ADB command */
    831 	for (i = 0; i <packet.data[0]-1; i++)
    832 		packet.data[i+2] = pmdata->data[i+4];
    833 	packet.unsol = 1;
    834 	packet.ack_only = 0;
    835 	adb_pass_up(&packet);
    836 }
    837 
    838 
    839 void
    840 pm_adb_restart()
    841 {
    842 	PMData p;
    843 
    844 	p.command = PMU_RESET_CPU;
    845 	p.num_data = 0;
    846 	p.s_buf = p.data;
    847 	p.r_buf = p.data;
    848 	pmgrop(&p);
    849 }
    850 
    851 void
    852 pm_adb_poweroff()
    853 {
    854 	PMData p;
    855 
    856 	p.command = PMU_POWER_OFF;
    857 	p.num_data = 4;
    858 	p.s_buf = p.data;
    859 	p.r_buf = p.data;
    860 	strcpy(p.data, "MATT");
    861 	pmgrop(&p);
    862 }
    863 
    864 void
    865 pm_read_date_time(t)
    866 	u_long *t;
    867 {
    868 	PMData p;
    869 
    870 	p.command = PMU_READ_RTC;
    871 	p.num_data = 0;
    872 	p.s_buf = p.data;
    873 	p.r_buf = p.data;
    874 	pmgrop(&p);
    875 
    876 	memcpy(t, p.data, 4);
    877 }
    878 
    879 void
    880 pm_set_date_time(t)
    881 	u_long t;
    882 {
    883 	PMData p;
    884 
    885 	p.command = PMU_SET_RTC;
    886 	p.num_data = 4;
    887 	p.s_buf = p.r_buf = p.data;
    888 	memcpy(p.data, &t, 4);
    889 	pmgrop(&p);
    890 }
    891 
    892 int
    893 pm_read_brightness()
    894 {
    895 	PMData p;
    896 
    897 	p.command = PMU_READ_BRIGHTNESS;
    898 	p.num_data = 1;		/* XXX why 1? */
    899 	p.s_buf = p.r_buf = p.data;
    900 	p.data[0] = 0;
    901 	pmgrop(&p);
    902 
    903 	return p.data[0];
    904 }
    905 
    906 void
    907 pm_set_brightness(val)
    908 	int val;
    909 {
    910 	PMData p;
    911 
    912 	val = 0x7f - val / 2;
    913 	if (val < 0x08)
    914 		val = 0x08;
    915 	if (val > 0x78)
    916 		val = 0x78;
    917 
    918 	p.command = PMU_SET_BRIGHTNESS;
    919 	p.num_data = 1;
    920 	p.s_buf = p.r_buf = p.data;
    921 	p.data[0] = val;
    922 	pmgrop(&p);
    923 }
    924 
    925 void
    926 pm_init_brightness()
    927 {
    928 	int val;
    929 
    930 	val = pm_read_brightness();
    931 	pm_set_brightness(val);
    932 }
    933 
    934 void
    935 pm_eject_pcmcia(slot)
    936 	int slot;
    937 {
    938 	PMData p;
    939 
    940 	if (slot != 0 && slot != 1)
    941 		return;
    942 
    943 	p.command = PMU_EJECT_PCMCIA;
    944 	p.num_data = 1;
    945 	p.s_buf = p.r_buf = p.data;
    946 	p.data[0] = 5 + slot;	/* XXX */
    947 	pmgrop(&p);
    948 }
    949 
    950 /*
    951  * Thanks to Paul Mackerras and Fabio Riccardi's Linux implementation
    952  * for a clear description of the PMU results.
    953  */
    954 static int
    955 pm_battery_info_smart(int battery, struct pmu_battery_info *info)
    956 {
    957 	PMData p;
    958 
    959 	p.command = PMU_SMART_BATTERY_STATE;
    960 	p.num_data = 1;
    961 	p.s_buf = p.r_buf = p.data;
    962 	p.data[0] = battery + 1;
    963 	pmgrop(&p);
    964 
    965 	info->flags = p.data[1];
    966 
    967 	info->secs_remaining = 0;
    968 	switch (p.data[0]) {
    969 	case 3:
    970 	case 4:
    971 		info->cur_charge = p.data[2];
    972 		info->max_charge = p.data[3];
    973 		info->draw = *((signed char *)&p.data[4]);
    974 		info->voltage = p.data[5];
    975 		break;
    976 	case 5:
    977 		info->cur_charge = ((p.data[2] << 8) | (p.data[3]));
    978 		info->max_charge = ((p.data[4] << 8) | (p.data[5]));
    979 		info->draw = *((signed short *)&p.data[6]);
    980 		info->voltage = ((p.data[8] << 8) | (p.data[7]));
    981 		break;
    982 	default:
    983 		/* XXX - Error condition */
    984 		info->cur_charge = 0;
    985 		info->max_charge = 0;
    986 		info->draw = 0;
    987 		info->voltage = 0;
    988 		break;
    989 	}
    990 	if (info->draw) {
    991 		if (info->flags & PMU_PWR_AC_PRESENT && info->draw > 0) {
    992 			info->secs_remaining =
    993 				((info->max_charge - info->cur_charge) * 3600)
    994 				/ info->draw;
    995 		} else {
    996 			info->secs_remaining =
    997 				(info->cur_charge * 3600) / -info->draw;
    998 		}
    999 	}
   1000 
   1001 	return 1;
   1002 }
   1003 
   1004 static int
   1005 pm_battery_info_legacy(int battery, struct pmu_battery_info *info, int ty)
   1006 {
   1007 	PMData p;
   1008 	long pcharge=0, charge, vb, vmax, chargemax;
   1009 	long vmax_charging, vmax_charged, amperage, voltage;
   1010 
   1011 	p.command = PMU_BATTERY_STATE;
   1012 	p.num_data = 0;
   1013 	p.s_buf = p.r_buf = p.data;
   1014 	pmgrop(&p);
   1015 
   1016 	info->flags = p.data[0];
   1017 
   1018 	if (info->flags & PMU_PWR_BATT_PRESENT) {
   1019 		if (ty == BATT_COMET) {
   1020 			vmax_charging = 213;
   1021 			vmax_charged = 189;
   1022 			chargemax = 6500;
   1023 		} else {
   1024 			/* Experimental values */
   1025 			vmax_charging = 365;
   1026 			vmax_charged = 365;
   1027 			chargemax = 6500;
   1028 		}
   1029 		vmax = vmax_charged;
   1030 		vb = (p.data[1] << 8) | p.data[2];
   1031 		voltage = (vb * 256 + 72665) / 10;
   1032 		amperage = (unsigned char) p.data[5];
   1033 		if ((info->flags & PMU_PWR_AC_PRESENT) == 0) {
   1034 			if (amperage > 200)
   1035 				vb += ((amperage - 200) * 15)/100;
   1036 		} else if (info->flags & PMU_PWR_BATT_CHARGING) {
   1037 			vb = (vb * 97) / 100;
   1038 			vmax = vmax_charging;
   1039 		}
   1040 		charge = (100 * vb) / vmax;
   1041 		if (info->flags & PMU_PWR_PCHARGE_RESET) {
   1042 			pcharge = (p.data[6] << 8) | p.data[7];
   1043 			if (pcharge > chargemax)
   1044 				pcharge = chargemax;
   1045 			pcharge *= 100;
   1046 			pcharge = 100 - pcharge / chargemax;
   1047 			if (pcharge < charge)
   1048 				charge = pcharge;
   1049 		}
   1050 		info->cur_charge = charge;
   1051 		info->max_charge = 100;
   1052 		info->draw = -amperage;
   1053 		info->voltage = voltage;
   1054 		if (amperage > 0)
   1055 			info->secs_remaining = (charge * 16440) / amperage;
   1056 		else
   1057 			info->secs_remaining = 0;
   1058 	} else {
   1059 		info->cur_charge = 0;
   1060 		info->max_charge = 0;
   1061 		info->draw = 0;
   1062 		info->voltage = 0;
   1063 		info->secs_remaining = 0;
   1064 	}
   1065 
   1066 	return 1;
   1067 }
   1068 
   1069 int
   1070 pm_battery_info(int battery, struct pmu_battery_info *info)
   1071 {
   1072 
   1073 	if (battery > pmu_nbatt)
   1074 		return 0;
   1075 
   1076 	switch (pmu_batt_type) {
   1077 	case BATT_COMET:
   1078 	case BATT_HOOPER:
   1079 		return pm_battery_info_legacy(battery, info, pmu_batt_type);
   1080 
   1081 	case BATT_SMART:
   1082 		return pm_battery_info_smart(battery, info);
   1083 	}
   1084 
   1085 	return 0;
   1086 }
   1087 
   1088 int
   1089 pm_read_nvram(addr)
   1090 	int addr;
   1091 {
   1092 	PMData p;
   1093 
   1094 	p.command = PMU_READ_NVRAM;
   1095 	p.num_data = 2;
   1096 	p.s_buf = p.r_buf = p.data;
   1097 	p.data[0] = addr >> 8;
   1098 	p.data[1] = addr;
   1099 	pmgrop(&p);
   1100 
   1101 	return p.data[0];
   1102 }
   1103 
   1104 void
   1105 pm_write_nvram(addr, val)
   1106 	int addr, val;
   1107 {
   1108 	PMData p;
   1109 
   1110 	p.command = PMU_WRITE_NVRAM;
   1111 	p.num_data = 3;
   1112 	p.s_buf = p.r_buf = p.data;
   1113 	p.data[0] = addr >> 8;
   1114 	p.data[1] = addr;
   1115 	p.data[2] = val;
   1116 	pmgrop(&p);
   1117 }
   1118