Source to bsd/dev/ppc/drvPMU/pmu.m


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#import "pmu.h"
#import "pmupriv.h"
#import "pmumisc.h"
#import "pmutables.h"
#import <kern/clock.h>
#import <kernserv/prototypes.h>
#import <kernserv/clock_timer.h>
#import <kernserv/ns_timer.h>
#import <bsd/sys/time.h>
#import <sys/callout.h>
#import <machdep/ppc/proc_reg.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/kernelDriver.h>
#import <driverkit/interruptMsg.h>

// extern to let us fix up the boot time.
extern void set_boot_time(void);

void gotInterruptCause(id, UInt32, UInt32, UInt8 *);
void timer_expired(port_t mach_port);

extern void kprintf(const char *, ...);
extern void bcopy(void *, void *, int);
extern msg_send_from_kernel(msg_header_t *, int, int);

extern id ApplePMUId;

@implementation ApplePMU

// **********************************************************************************
// probe
//
// 
//
// **********************************************************************************
+ (Boolean) probe : deviceDescription
{
  id dev;
kprintf("PMU probe\n");
  if ( (dev = [ self alloc ]) == nil ) {
    return NO;
  }
  
  if ([dev initFromDeviceDescription:deviceDescription] == nil) {
    return NO;
  }
  
  ApplePMUId = dev;
  
  set_boot_time();

  return YES;
}




// **********************************************************************************
// initFromDeviceDescription
//
// 
//
// **********************************************************************************
- initFromDeviceDescription:(IODeviceDescription *)deviceDescription
{
  VIAAddress	  physicalAddress;
  IORange         *ioRange;
  PMUmachMessage  theRequest;
  unsigned int    *oldIRQs, newIRQs[2], tmpIRQ;
  
  
  if ( [super initFromDeviceDescription:deviceDescription] == nil ) {
    [self free];
    return nil;
  }
  
  [self setDeviceKind:"PM Subsystem"];
  [self setLocation:NULL];
  [self setName:"PMU"];
  
  ioRange = [deviceDescription memoryRangeList];
  physicalAddress = (VIAAddress)ioRange->start;
  
  VIA1_shift		= physicalAddress + 0x1400; // initialize VIA addresses
  VIA1_auxillaryControl = physicalAddress + 0x1600;
  VIA1_interruptFlag	= physicalAddress + 0x1A00;
  VIA1_interruptEnable	= physicalAddress + 0x1C00;
// VIA2_dataB		= physicalAddress + 0x2000;		// 5300
// PMreq		= 1 << M2Req;
// PMack		= 1 << M2Ack;
  VIA2_dataB		= physicalAddress + 0x0000; // Hooper uses VIA 1 instead
  PMreq			= 1 << HooperReq;           // and different bits in it as well
  PMack			= 1 << HooperAck;
  // initialize other variables
  ADBclient = NULL;
  RTCclient = NULL;
  debugging = FALSE;
  queueHead = NULL;
  queueTail = NULL;
  PGE_ISR_state = kPMUidle;
  pollList = 0;
  autopollOn = FALSE;
  adb_reading = FALSE;
  PMU_int_pending = FALSE;

  adb_read_timeout = 100000000;

  [self AcknowledgePMUInterrupt];                 		// turn off any pending PGE interrupt
  [self EnablePMUInterrupt];              			// enable PGE interrupts

  // This is a still sleazy hack...
  oldIRQs = [deviceDescription interruptList];

  // This is another sleazy hack. The second irq is two lower in the via table.
  tmpIRQ = ((*oldIRQs ^ 0x18) + 2) ^ 0x18;

  newIRQs[0] = *oldIRQs;
  newIRQs[1] = tmpIRQ;

  [deviceDescription setInterruptList:newIRQs num:2];

  [self enableAllInterrupts];

  if ([self startIOThread] != IO_R_SUCCESS) {
    [self free];
    return nil;
  }

  port = IOConvertPort([self interruptPort],IO_KernelIOTask,IO_Kernel);
  
  theRequest.msgBody.pmCommand = kPMUSetModem1SecInt;             // tell PGE why it may interrupt
  theRequest.msgBody.pmFlag = FALSE;
  theRequest.msgBody.pmSLength1 = 1;
  theRequest.msgBody.pmSBuffer1[0] = kPMUMD2Int | kPMUbrightnessInt | kPMUADBint;
  theRequest.msgBody.pmSLength2 = 0;
  theRequest.msgBody.pmCallback = NULL;
  
  theRequest.msgHeader.msg_simple = TRUE;
  theRequest.msgHeader.msg_type = MSG_TYPE_NORMAL;
  theRequest.msgHeader.msg_remote_port = port;
  theRequest.msgHeader.msg_local_port = PORT_NULL;
  theRequest.msgHeader.msg_size = sizeof(PMUmachMessage);
  msg_send_from_kernel(&theRequest.msgHeader, MSG_OPTION_NONE, 0);
  
  theRequest.msgBody.pmCommand = kPMUreadINT;                     // read any pending interrupt from PGE
  theRequest.msgBody.pmFlag = FALSE;
  theRequest.msgBody.pmSLength1 = 0;                              // just to clear it
  theRequest.msgBody.pmSLength2 = 0;
  theRequest.msgBody.pmRBuffer = &interruptState[0];
  theRequest.msgBody.pmCallback = NULL;
  
  theRequest.msgHeader.msg_simple = TRUE;
  theRequest.msgHeader.msg_type = MSG_TYPE_NORMAL;
  theRequest.msgHeader.msg_remote_port = port;
  theRequest.msgHeader.msg_local_port = PORT_NULL;
  theRequest.msgHeader.msg_size = sizeof(PMUmachMessage);
  msg_send_from_kernel(&theRequest.msgHeader, MSG_OPTION_NONE, 0);

  [self registerDevice];

  return self;
}


// **********************************************************************************
// free
//
// 
//
// **********************************************************************************
- free
{
return [ super free ];
}





// **********************************************************************************
// poll_device
//
// System interrupts are disabled, but we are still operating the PMU for mini-
// monitor keyboard input.  We are called here in a loop to service the PMU.
//
// **********************************************************************************
- (void)poll_device
{
  if ( *VIA1_interruptFlag & 0x04 ) {   // is shift register done? ( ifSR )
    [self interruptOccurred];         	// yes, handle it
    return;
  }
  if ( *VIA1_interruptFlag & 0x10 ) { 	// is PMU requesting service? ( ifCB1 )
    *VIA1_interruptFlag = 0x10;       	// yes, clear interrupt ( ifCB1 )
    PGE_ISR_state = kPMUidle;     	// and handle it
    [self interruptOccurredAt:1];
  }
}


// **********************************************************************************
// receiveMsg
//
// 
//
// **********************************************************************************
- (void)receiveMsg
{
PMUmachMessage * toQueue;
IOReturn result;
if ( (PGE_ISR_state == kPMUidle) && !adb_reading ) {
	localMachMessage.msgHeader.msg_size = sizeof(PMUmachMessage);
        localMachMessage.msgHeader.msg_local_port = [self interruptPort];
	result = msg_receive(&localMachMessage.msgHeader, (msg_option_t)RCV_TIMEOUT, 0);
	if ( result == RCV_SUCCESS ) {
		[self StartPMUTransmission:&localMachMessage.msgBody];
		}
	}
else {
	toQueue = (PMUmachMessage*)kalloc(sizeof(PMUmachMessage));
        toQueue->msgHeader.msg_size = sizeof(PMUmachMessage);
        toQueue->msgHeader.msg_local_port = [self interruptPort];
        result = msg_receive(&toQueue->msgHeader, (msg_option_t)RCV_TIMEOUT, 0);
        if ( result == RCV_SUCCESS ) {
		toQueue->msgBody.prev = queueTail;
		toQueue->msgBody.next = NULL;
		if ( queueTail != NULL ) {
			queueTail->msgBody.next = toQueue;
			}
		else {
			queueHead = toQueue;
			}
		queueTail = toQueue;
		}
	}
}


// **********************************************************************************
// timeoutOccurred
//
// Our adb-read timer has expired after sending an adb-read command to the PMU.
// This means there is no such addressed device on the ADB bus.
// We call back to the ADB driver with a zero-characters-received response and
// dequeue our command queue and carry on.
// **********************************************************************************
- (void)timeoutOccurred
{
adb_reading = FALSE;
if ( clientRequest->pmCallback != NULL ) {		// Make the client callback
	clientRequest->pmCallback(clientRequest->pmId, clientRequest->pmRefNum, 0, NULL);
	}						// with zero received-length
[self CheckRequestQueue];
}


// ****************************************************************************
//      CheckRequestQueue
//      Called at interrupt time when current request is complete.  We may start
//      another request here if one is in queue, or we may re-enable PMU interrupts
//      (they were turned off in PMUStartIO) and return.
// ****************************************************************************
- (void)CheckRequestQueue
{
PMUmachMessage * nextRequest;

if ( queueHead == NULL ) {		                // is queue empty?
        [self EnablePMUInterrupt];                      // yes, enable interrupt and return
        }
else {
        nextRequest = queueHead;                        // no, dequeue first command
        queueHead = nextRequest->msgBody.next;
	if ( queueHead == NULL ) {
		queueTail = NULL;
		}
	bcopy (&nextRequest->msgBody, &localMachMessage.msgBody, sizeof(PMURequest));	// copy it
        kfree(nextRequest, sizeof(PMUmachMessage));				     	// free its memory
        [self StartPMUTransmission:&localMachMessage.msgBody];				// and send it to the PMU
        }
}


// **********************************************************************************
// registerForADBAutopoll
//
// The ADB driver is calling to tell us that it is prepared to receive
// "unsolicited" ADB autopoll data.  The parameter tells who to call
// when we get some.
//
// **********************************************************************************
- (void)registerForADBAutopoll	:(pmADBinput_func)InputHandler
				:(id)caller
{
ADBclient = InputHandler;
ADBid = caller;
}


// **********************************************************************************
// ADBWrite
//
// **********************************************************************************
- (PMUStatus)ADBWrite	:(UInt32)DevAddr
			:(UInt32)DevReg
			:(UInt32)ByteCount
			:(UInt8*)Buffer
			:(UInt32)RefNum
			:(id)Id
			:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

if ( 	(ByteCount == 0) ||
	(Buffer == NULL) ||
	(ByteCount > (MISC_LENGTH-3) ) ) {
		return kPMUParameterError;
		}

request.msgBody.pmCommand = kPMUpMgrADB;
request.msgBody.pmFlag = TRUE;			// this op solicits input from PGE
request.msgBody.pmSLength1 = 3;
request.msgBody.pmSBuffer2 = Buffer;
request.msgBody.pmSLength2 = ByteCount;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmCallback = Callback;
request.msgBody.pmId = Id;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmSBuffer1[0] = kPMUWriteADB | (DevAddr << kPMUADBAddressField) | (DevReg);
if ( autopollOn ) {
	request.msgBody.pmSBuffer1[1] = 2;
	}
else {
	request.msgBody.pmSBuffer1[1] = 0;
	}
request.msgBody.pmSBuffer1[2] = ByteCount;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	return kPMUNoError;
	}
else {
	return kPMUIOError;
	}
}


// **********************************************************************************
// ADBRead
//
// **********************************************************************************
- (PMUStatus)ADBRead	:(UInt32)DevAddr
			:(UInt32)DevReg
			:(UInt32)RefNum
                        :(id)Id
			:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

request.msgBody.pmCommand = kPMUpMgrADB;
request.msgBody.pmFlag = TRUE;			// this op solicits input from PGE
request.msgBody.pmSLength1 = 3;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmCallback = Callback;
request.msgBody.pmId = Id;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmSBuffer1[0] = kPMUReadADB | (DevAddr << kPMUADBAddressField) | (DevReg);
if ( autopollOn ) {
	request.msgBody.pmSBuffer1[1] = 2;
	}
else {
	request.msgBody.pmSBuffer1[1] = 0;
	}
request.msgBody.pmSBuffer1[2] = 0;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	return kPMUNoError;
	}
else {
	return kPMUIOError;
	}
}


// **********************************************************************************
// ADBReset
//
// **********************************************************************************
- (PMUStatus)ADBReset	:(UInt32)RefNum
                        :(id)Id
			:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

request.msgBody.pmCommand = kPMUpMgrADB;
request.msgBody.pmFlag = TRUE;		// this op solicits input from PGE
request.msgBody.pmSLength1 = 3;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmCallback = Callback;
request.msgBody.pmId = Id;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmSBuffer1[0] = kPMUResetADBBus;
request.msgBody.pmSBuffer1[1] = 0;
request.msgBody.pmSBuffer1[2] = 0;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	return kPMUNoError;
	}
else {
	return kPMUIOError;
	}
}


// **********************************************************************************
// ADBFlush
//
// **********************************************************************************
- (PMUStatus)ADBFlush	:(UInt32)DevAddr
			:(UInt32)RefNum
                        :(id)Id
			:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

request.msgBody.pmCommand = kPMUpMgrADB;
request.msgBody.pmFlag = TRUE;
request.msgBody.pmSLength1 = 3;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmId = Id;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmCallback = Callback;
request.msgBody.pmSBuffer1[0] = kPMUFlushADB | (DevAddr << kPMUADBAddressField);
if ( autopollOn ) {
	request.msgBody.pmSBuffer1[1] = 2;
	}
else {
	request.msgBody.pmSBuffer1[1] = 0;
	}
request.msgBody.pmSBuffer1[2] = 0;
		
request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	return kPMUNoError;
	}
else {
	return kPMUIOError;
	}
}


// **********************************************************************************
// ADBSetPollList
//
// **********************************************************************************
- (PMUStatus)ADBSetPollList	:(UInt32)PollBitField
				:(UInt32)RefNum
	                        :(id)Id
				:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

pollList = PollBitField;				// remember the new poll list

if ( autopollOn ) {					// if PMU is currently autopolling,
	request.msgBody.pmCommand = kPMUpMgrADB;	// give it the new list
	request.msgBody.pmFlag = FALSE;
	request.msgBody.pmSLength1 = 4;
	request.msgBody.pmSBuffer2 = NULL;
	request.msgBody.pmSLength2 = 0;
	request.msgBody.pmRBuffer = NULL;
	request.msgBody.pmRefNum = RefNum;
	request.msgBody.pmId = Id;
	request.msgBody.pmCallback = Callback;
	request.msgBody.pmSBuffer1[0] = 0;
	request.msgBody.pmSBuffer1[1] = 0x86;
	request.msgBody.pmSBuffer1[2] = (UInt8)(PollBitField >> 8);
	request.msgBody.pmSBuffer1[3] = (UInt8)(PollBitField & 0xff);

	request.msgHeader.msg_simple = TRUE;
	request.msgHeader.msg_type = MSG_TYPE_NORMAL;
	request.msgHeader.msg_remote_port = port;
	request.msgHeader.msg_local_port = PORT_NULL;
	request.msgHeader.msg_size = sizeof(PMUmachMessage);
	return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

	if ( return_code == SEND_SUCCESS ) {
		return kPMUNoError;
		}
	else {
		return kPMUIOError;
		}
	}
else {							// we'll do it later
	if ( Callback != NULL ) {			// but make the client callback now
		Callback(Id, RefNum, 0, NULL);
		}
	}
return kPMUNoError;
}


// **********************************************************************************
// ADBSetFileServerMode()
//
// **********************************************************************************
- (PMUStatus)ADBSetFileServerMode       :(UInt32)RefNum
                                		:(id)Id
                                		:(pmCallback_func)Callback

{
return kPMUNotSupported;
}


// **********************************************************************************
// ADBPollEnable
//
// **********************************************************************************
- (PMUStatus)ADBPollEnable      :(UInt32)RefNum
                                :(id)Id
                                :(pmCallback_func)Callback

{
PMUmachMessage  request;
msg_return_t    return_code;


request.msgBody.pmCommand = kPMUpMgrADB;        // give it the list we have
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 4;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;
request.msgBody.pmSBuffer1[0] = 0;
request.msgBody.pmSBuffer1[1] = 0x86;
request.msgBody.pmSBuffer1[2] = (UInt8)(pollList >> 8);
request.msgBody.pmSBuffer1[3] = (UInt8)(pollList & 0xff);

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	autopollOn = TRUE;
        return kPMUNoError;
        }
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// ADBPollDisable
//
// **********************************************************************************
- (PMUStatus)ADBPollDisable	:(UInt32)RefNum
	                        :(id)Id
				:(pmCallback_func)Callback
{
PMUmachMessage	request;
msg_return_t	return_code;

request.msgBody.pmCommand = kPMUpMgrADBoff;
request.msgBody.pmFlag = FALSE;
request.msgBody.pmFlag = TRUE;
request.msgBody.pmSLength1 = 0;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
	autopollOn = FALSE;
	return kPMUNoError;
	}
else {
	return kPMUIOError;
	}
}


// **********************************************************************************
// ADBSetPollRate
//
// **********************************************************************************
- (PMUStatus)ADBSetPollRate     :(UInt32)NewRate
				:(UInt32)RefNum
                                :(id)Id
                                :(pmCallback_func)Callback
{
return kPMUNotSupported;
}


// **********************************************************************************
// ADBGetPollRate
//
// **********************************************************************************
- (PMUStatus)ADBGetPollRate     :(UInt32 *)CurrentRate
                                :(UInt32)RefNum
                                :(id)Id
                                :(pmCallback_func)Callback
{
return kPMUNotSupported;
}


// **********************************************************************************
// ADBSetAlternateKeyboard
//
// **********************************************************************************
- (PMUStatus)ADBSetAlternateKeyboard	:(UInt32)DevAddr
                                	:(UInt32)RefNum
                                	:(id)Id
                                	:(pmCallback_func)Callback
{
return kPMUNotSupported;
}


// **********************************************************************************
// ADBinput
//
// The PGE has interrupted with ADB data.  We package this up and send
// it to our ADB client, if there is one, either as the result to its previous
// read command, or as autopoll data.
//
// **********************************************************************************
- (void)ADBinput:(UInt32)theLength:(UInt8 *)theInput
{
if ( theInput[0] & kPMUautopoll ) {				// autopoll data?
	if ( ADBclient != NULL ) {
	  ADBclient(ADBid, 0, (theInput[1]>>4)&0x0F, theLength-2, theInput+2);
	  // yes, call adb input handler
	}
	return;
	}
if ( adb_reading ) {						// no, expecting adb input?
	if ( clientRequest->pmSBuffer1[0] == theInput[1] ) {	// yes, is it our input?
		ns_untimeout((func)timer_expired,(void *)port);	// yes, turn off our timer
		if ( clientRequest->pmCallback != NULL ) {	// Make the client callback
			clientRequest->pmCallback(clientRequest->pmId, clientRequest->pmRefNum, theLength-2, theInput+2);
			}
		adb_reading = FALSE;
		return;
		}
	}
kprintf("unexpected adb input: %02d %02x %02x %02x %02x\n", theLength, interruptState[0], interruptState[1], interruptState[2], interruptState[3]);
}


// **********************************************************************************
// registerForClockTicks
//
// The RTC driver is calling to tell us that it is prepared to receive clock
// ticks every second.  The parameter block tells who to call when we get one.
//
// **********************************************************************************
- (void)registerForClockTicks	:(pmCallback_func)TickHandler
				:(id)caller
{
RTCclient = TickHandler;
RTCid = caller;
}


// **********************************************************************************
// setRealTimeClock
//
// The RTC driver is calling to set the real time clock.  We translate this into
// a PMU command and enqueue it to our command queue.
//
// **********************************************************************************
- (PMUStatus)setRealTimeClock	:(UInt8 *)newTime
				:(UInt32)RefNum
	                        :(id)Id
                                :(pmCallback_func)Callback
{
PMUmachMessage  request;
msg_return_t    return_code;

if ( newTime == NULL ) {
	return kPMUParameterError;
	}

request.msgBody.pmCommand = kPMUtimeWrite;
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 0;
request.msgBody.pmSBuffer2 = newTime;
request.msgBody.pmSLength2 = 4;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
        return kPMUNoError;
        }
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// getRealTimeClock
//
// The RTC driver is calling to read the real time clock.  We translate this into
// a PMU command and enqueue it to our command queue.
//
// **********************************************************************************
- (PMUStatus)getRealTimeClock	:(UInt8 *)currentTime
                                :(UInt32)RefNum
	                        :(id)Id
                                :(pmCallback_func)Callback
{
PMUmachMessage  request;
msg_return_t    return_code;

if ( currentTime == NULL ) {
        return kPMUParameterError;
        }

request.msgBody.pmCommand = kPMUtimeRead;
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 0;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = currentTime;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
        return kPMUNoError;
        }
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// readNVRAM
//
// The NVRAM driver is calling to read part of the NVRAM.  We translate this into
// single-byte PMU commands and enqueue them to our command queue.
//
// **********************************************************************************
- (PMUStatus) readNVRAM :(UInt32)Offset
                        :(UInt32)Length
			:(UInt8 *)Buffer
                        :(UInt32)RefNum
                        :(id)Id
                        :(pmCallback_func)Callback
{
PMUmachMessage  request;
msg_return_t    return_code;
int		i;
UInt8 *		client_buffer = Buffer;
UInt32		our_offset = Offset;

if ( (Buffer == NULL) ||
	(Length == 0) ||
	(Length > 8192) ||
	(Offset > 8192) ||
	((Length + Offset) > 8192) ) {
        return kPMUParameterError;
        }

for ( i = 0; i < (Length - 1); i++ ) {			// read all but the last byte
	request.msgBody.pmCommand = kPMUNVRAMRead;
	request.msgBody.pmFlag = FALSE;
	request.msgBody.pmSLength1 = 2;
	request.msgBody.pmSBuffer2 = NULL;
	request.msgBody.pmSLength2 = 0;
	request.msgBody.pmRBuffer = client_buffer++;
	request.msgBody.pmCallback = NULL;
	request.msgBody.pmSBuffer1[0] = our_offset >> 8;
	request.msgBody.pmSBuffer1[1] = our_offset++;

	request.msgHeader.msg_simple = TRUE;
	request.msgHeader.msg_type = MSG_TYPE_NORMAL;
	request.msgHeader.msg_remote_port = port;
	request.msgHeader.msg_local_port = PORT_NULL;
	request.msgHeader.msg_size = sizeof(PMUmachMessage);
	return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

	if ( return_code != SEND_SUCCESS ) {
        	return kPMUIOError;
        	}
	}

request.msgBody.pmCommand = kPMUNVRAMRead;			// now read last byte
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 2;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = client_buffer;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;
request.msgBody.pmSBuffer1[0] = our_offset >> 8;
request.msgBody.pmSBuffer1[1] = our_offset;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
 	return kPMUNoError;
      	}
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// writeNVRAM
//
// The NVRAM driver is calling to write part of the NVRAM.  We translate this into
// single-byte PMU commands and enqueue them to our command queue.
//
// **********************************************************************************
- (PMUStatus) writeNVRAM:(UInt32)Offset
                        :(UInt32)Length
			:(UInt8 *)Buffer
                        :(UInt32)RefNum
                        :(id)Id
                        :(pmCallback_func)Callback
{
PMUmachMessage  request;
msg_return_t    return_code;
int		i;
UInt32		our_offset = Offset;
UInt8 *		client_buffer = Buffer;

if ( (Buffer == NULL) ||
        (Length == 0) ||
        (Length > 8192) ||
        (Offset > 8192) ||
        ((Length + Offset) > 8192) ) {
        return kPMUParameterError;
        }

for ( i = 0; i < (Length - 1); i++ ) {			// write all but the last byte
        request.msgBody.pmCommand = kPMUNVRAMWrite;
	request.msgBody.pmFlag = FALSE;
        request.msgBody.pmSLength1 = 3;
        request.msgBody.pmSBuffer2 = NULL;
        request.msgBody.pmSLength2 = 0;
        request.msgBody.pmRBuffer = NULL;
        request.msgBody.pmCallback = NULL;
	request.msgBody.pmSBuffer1[0] = our_offset >> 8;
	request.msgBody.pmSBuffer1[1] = our_offset++;
	request.msgBody.pmSBuffer1[2] = *client_buffer++;

	request.msgHeader.msg_simple = TRUE;
	request.msgHeader.msg_type = MSG_TYPE_NORMAL;
	request.msgHeader.msg_remote_port = port;
	request.msgHeader.msg_local_port = PORT_NULL;
	request.msgHeader.msg_size = sizeof(PMUmachMessage);
	return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

        if ( return_code != SEND_SUCCESS ) {
                return kPMUIOError;
                }
	}

request.msgBody.pmCommand = kPMUNVRAMWrite;		// write the last byte
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 3;
request.msgBody.pmSBuffer2 = NULL;
request.msgBody.pmSLength2 = 0;
request.msgBody.pmRBuffer = NULL;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;
request.msgBody.pmSBuffer1[0] = our_offset >> 8;
request.msgBody.pmSBuffer1[1] = our_offset;
request.msgBody.pmSBuffer1[2] = *client_buffer;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
        return kPMUNoError;
        }
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// registerForPowerInterrupts
//
// Some driver is calling to say it is prepared to receive "unsolicited" power-system
// interrups (e.g. battery low).  The parameter block says who to call when we get one.
//
// **********************************************************************************
- (void)registerForPowerInterrupts	:(pmCallback_func)buttonHandler
					:(id)caller
{
PWRclient = buttonHandler;
PWRid = caller;
}


// **********************************************************************************
// sendMiscCommand
//
// Some driver is calling to send some miscellaneous command.  We copy this into a
// PMU command and enqueue it to our command queue.
//
// **********************************************************************************
- (PMUStatus)sendMiscCommand	:(UInt32)Command
				:(UInt32)SLength
				:(UInt8 *)SBuffer
				:(UInt8 *)RBuffer
				:(UInt32)RefNum
	                        :(id)Id
				:(pmCallback_func)Callback
{
PMUmachMessage  request;
msg_return_t    return_code;
SInt32		rsp_length;
SInt32		send_length;

rsp_length = rspLengthTable[Command];                         	// get cmd and response lengths from table
send_length = cmdLengthTable[Command];

if ( ((SLength != 0) && (SBuffer == NULL)) ||          		// validate pointers
	((rsp_length != 0) && (RBuffer == NULL)) ) {
		return kPMUParameterError;
		}
if ( (Command != kPMUdownloadFlash) &&
	((send_length != -1) && (send_length != SLength)) ) {
                return kPMUParameterError;
                }

if ( send_length > MISC_LENGTH ) {
	return kPMUParameterError;
	}

request.msgBody.pmCommand = Command;
request.msgBody.pmFlag = FALSE;
request.msgBody.pmSLength1 = 0;
request.msgBody.pmSBuffer2 = SBuffer;
request.msgBody.pmSLength2 = SLength;
request.msgBody.pmRBuffer = RBuffer;
request.msgBody.pmRefNum = RefNum;
request.msgBody.pmId = Id;
request.msgBody.pmCallback = Callback;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_remote_port = port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(PMUmachMessage);
return_code = msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

if ( return_code == SEND_SUCCESS ) {
        return kPMUNoError;
        }
else {
        return kPMUIOError;
        }
}


// **********************************************************************************
// StartPMUTransmission
//
// Transmission of the command byte is started.  The transaction will be
// completed by the Shift Register Interrupt Service Routine.
// **********************************************************************************
- (void)StartPMUTransmission:(PMURequest *)plugInMessage
{
if ( !debugging ) {
	clientRequest = plugInMessage;
	firstChar = plugInMessage->pmCommand;		// get command byte
	charCountS1 = plugInMessage->pmSLength1;	// get caller's length counters
	charCountS2 = plugInMessage->pmSLength2;
	dataPointer1 = plugInMessage->pmSBuffer1;	// and transmit data pointers
	dataPointer2 = plugInMessage->pmSBuffer2;
	dataPointer = plugInMessage->pmRBuffer;		// set up read pointer for data bytes
	charCountR = rspLengthTable[firstChar];		// get response length from table
	charCountR2 = charCountR;

				// figure out what happens after command byte transmission
	if ( cmdLengthTable[firstChar] < 0 ) {	// will we be sending a length byte next?
		PGE_ISR_state = kPMUxmtLen;	// yes
		}
	else {					// no, will we be sending data next?
		if ( cmdLengthTable[firstChar] > 0 ) {
			PGE_ISR_state = kPMUxmtData;			// yes
			}
		else {							// no, will we be receiving a length byte next?
			if ( charCountR < 0 ) {
				PGE_ISR_state = kPMUreadLen_cmd;	// yes
				}
			else {						// no, will we be receiving data next?
				if ( charCountR > 0 ) {
					PGE_ISR_state = kPMUreadData;	// yes
					}
				else {
					PGE_ISR_state = kPMUdone;	// no, this is a single-byte transaction
					}
				}
			}
		}
								// ready to start the command byte
	*VIA1_auxillaryControl |= 0x1C;				// set shift register to output
	*VIA1_shift = firstChar;				// give it the byte (this clears any pending SR interrupt)
//	*VIA1_interruptEnable = 0x84;				// enable SR interrupt
	*VIA2_dataB &= ~PMreq;					// assert /REQ
	return;
	
	}
else {
	UInt32		i;
	
	*VIA1_interruptEnable = 0x04;							// disable SR interrupt

	firstChar = plugInMessage->pmCommand;						// get command byte
	charCountS1 = plugInMessage->pmSLength1;					// get caller's length counters
	charCountS2 = plugInMessage->pmSLength2;
	dataPointer1 = plugInMessage->pmSBuffer1;					// and transmit data pointers
	dataPointer2 = plugInMessage->pmSBuffer2;
	
	charCountR = rspLengthTable[firstChar];						// get response length from table
	charCountR2 = charCountR;
	
	[self SendPMUByte:firstChar];							// send command byte

	if ( cmdLengthTable[firstChar] < 0 ) {						// should we send a length byte?
		[self SendPMUByte:(UInt8)(charCountS1 + charCountS2)];			// yes, do it
		}

	for ( i = 0; i < charCountS1; i++ ) {						// send data bytes
		[self SendPMUByte:*dataPointer1++];
		}

	for ( i = 0; i < charCountS2; i++ ) {						// send more data bytes
		[self SendPMUByte:*dataPointer2++];
		}
/* charCountR ==	0:	no reply at all
				1:	only a reply byte will be sent by the PGE
				<0: a length byte and a reply will be sent
				>1: a reply will be sent, but no length byte
					 (length is charCount - 1)
*/
	if ( charCountR ) {								// receive the reply byte
		if ( charCountR == 1 ) {		
			[self ReadPMUByte:plugInMessage->pmRBuffer];
			}
		else {
			if ( charCountR < 0 ) {						// receive the length byte
				[self ReadPMUByte:&receivedByte];
				charCountR = receivedByte;
				}
			else {
				charCountR--;
				}
			dataPointer = plugInMessage->pmRBuffer;
			for ( i = 0; i < charCountR; i++ ) {
				[self ReadPMUByte:dataPointer++];			// receive the rest of the reply
				}
			}
		}

	if ( plugInMessage->pmCallback != NULL ) {				// Make the client callback
		plugInMessage->pmCallback(plugInMessage->pmId, plugInMessage->pmRefNum, charCountR, plugInMessage->pmRBuffer);
		}
	return;
	}
}



// ****************************************************************************
//	interruptOccurred
//	The shift register has finished shifting in a byte from PG&E or finished
//	shifting out a byte to PG&E.  Here we continue the transaction by starting
//	the i/o of the next byte, or we finish the transaction by calling the
//	client's callback function.
//	Both the VIA interrupt flag register and the interrupt enable registers
//	have been cleared by the ohare ISR.
// ****************************************************************************

- (void)interruptOccurred
{
*VIA2_dataB |= PMreq;					// deassert /REQ line
							// what state are we in?
switch ( PGE_ISR_state ) {
							// We are processing a PMU interrupt.  We are reading the response
							// to the kPMUreadINT command, and a byte has arrived.
	case kPMUrcvData_int:
		*dataPointer++ = *VIA1_shift;		// read the data byte
		charCountR2--;
		if ( charCountR2 > 0 ) {				// is there more to read?
			while ( !(*VIA2_dataB & PMack) ) {
				}
			*VIA2_dataB &= ~PMreq;				// yes, assert /REQ
//			*VIA1_interruptEnable = 0x84;			// enable SR interrupt
			return;						// next interrupt will be next data byte
			}
		if ( interruptState[0] & kPMUADBint ) {					// no, what kind of interrupt was it?
			[self ADBinput: (UInt32)charCountR: &interruptState[0]];	// ADB
			}
		else {
			if ( interruptState[0] & kPMUbattInt ) {
				kprintf("battery PGE interrupt\n");
				}
			else {
				if ( interruptState[0] & kPMUoneSecInt ) {
//					kprintf("one-second PGE interrupt\n");
					if ( RTCclient != NULL ) {			// one-second interrupt
        					RTCclient(RTCid,0,0,0);
        					}
					}
				else {
					if ( interruptState[0] & kPMUenvironmentInt ) {
						kprintf("environment interrupt\n");
						}
					else {
						if ( interruptState[0] & kPMUbrightnessInt ) {
							kprintf("brightness button PGE interrupt\n");
							}
						else {
							kprintf("machine-dependent PGE interrupt\n");
							}
						}
					}
				}
			}
		PGE_ISR_state = kPMUidle;					// set the state
		if ( !PMU_int_pending ) {					// is PMU requesting service again?
//		if ( !(*VIA1_interruptFlag & 0x10) ) {				// is PMU requesting service again? ( ifCB1 )
//			if ( queueHead == (PMUmachMessage*)0 ) {		// no, queue empty?
//				*VIA1_interruptEnable = 0x90;			// yes, enable PMU interrupts ( ifCB1 )
//				return;						// and we are completely idle
//				}
			[self CheckRequestQueue];				// no, start next queued transaction
			}
		else {
//			*VIA1_interruptFlag = 0x10;		// PMU wants service, acknowledge VIA interrupt ( ifCB1 )	
			PMU_int_pending = FALSE;
			*VIA1_auxillaryControl |= 0x1C;				// set shift register to output
			*VIA1_shift = kPMUreadINT;				// give it the command byte
			*VIA2_dataB &= ~PMreq;					// assert /REQ
//			*VIA1_interruptEnable = 0x84;				// enable SR interrupt
			PGE_ISR_state = kPMUreadLen_int;			// set the state
			dataPointer = &interruptState[0];			// set up read pointer for data bytes
			return;					// next interrupt is command byte transmission complete
			}
		return;
		
							// We are processing a PMU interrupt.
							// We have finished transmitting the kPMUreadINT command byte, and
							// according to our table, we will be getting a response and a
							// length byte for it.  Finish the transmit handshake and set up
	case kPMUreadLen_int:				// a receive for the length byte.
		receivedByte = *VIA1_shift;					// read shift reg to turn off SR int
		PGE_ISR_state = kPMUrcvLen_int;
		*VIA1_auxillaryControl &= 0xEF;					// set shift register to input
		while ( !(*VIA2_dataB & PMack) ) {
			}
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt will be the length byte

							// We are processing a PMU interrupt.
	case kPMUrcvLen_int:				// The length byte has arrived.  Read it and start data read
		charCountR = *VIA1_shift;		// read it

		charCountR2 = charCountR;
		PGE_ISR_state = kPMUrcvData_int;
		while ( !(*VIA2_dataB & PMack) ) {
			}
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt will be the first data byte
			
							// We are doing a command transaction.  The command byte transmission
	case kPMUxmtLen:				// has completed.  Start length byte transmission
		PGE_ISR_state = kPMUxmtData;
		while ( !(*VIA2_dataB & PMack) ) {
			}
		*VIA1_shift = (UInt8)(charCountS1 + charCountS2);		// give it the length byte
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt start sending data
		
							// We are doing a command transaction.  A byte transmission has completed.
	case kPMUxmtData:				// Continue data byte transmission
		while ( !(*VIA2_dataB & PMack) ) {
			}
		if ( charCountS1 ) {
			*VIA1_shift = *dataPointer1++;				// give it the next data byte from buffer 1
			*VIA2_dataB &= ~PMreq;					// assert /REQ 
			if ( --charCountS1 + charCountS2 ) {
//				*VIA1_interruptEnable = 0x84;			// enable SR interrupt
				return;						// next interrupt do another byte
				}
			}
		else {
			if ( charCountS2 ) {
				*VIA1_shift = *dataPointer2++;		// buffer 1 empty, give it the next byte from buffer 2
				*VIA2_dataB &= ~PMreq;			// assert /REQ
				if ( --charCountS2 ) {
//					*VIA1_interruptEnable = 0x84;	// enable SR interrupt
					return;				// next interrupt do another byte
					}
				}
			}
										// sending last byte, what's next?
		if ( charCountR < 0 ) {
			PGE_ISR_state = kPMUreadLen_cmd;			// we will receive a length byte
			}
		else {
			if ( charCountR > 0 ) {
				PGE_ISR_state = kPMUreadData;			// we will receive constant-length data
				}
			else {
				PGE_ISR_state = kPMUdone;			// nothing, we're done
				}
			}
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;
		
							// We have finished the transmission part of a command transaction, and
							// according to our table, we will be getting a response and a
							// length byte for it.  Finish the transmit handshake and set up
	case kPMUreadLen_cmd:				// a receive for the length byte.
		receivedByte = *VIA1_shift;					// read shift reg to turn off SR int
		PGE_ISR_state = kPMUrcvLen_cmd;
		*VIA1_auxillaryControl &= 0xEF;					// set shift register to input
		while ( !(*VIA2_dataB & PMack) ) {
			}
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt will be the length byte

	case kPMUrcvLen_cmd:				// the length byte has arrived, read it and start data read
		charCountR = *VIA1_shift;				// read it
		charCountR2 = charCountR;
		PGE_ISR_state = kPMUrcvData_cmd;
		if ( !(*VIA2_dataB & PMack) )
			if ( ![self WaitForAckHi] ) {
				return;						// make sure ACK is high
				}
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt will be the first data byte
	
		
							// We have finished the transmission part of a command transaction, and
							// according to our table, we will be getting a response but not a
							// length byte for it.  Finish the transmit handshake and set up
	case kPMUreadData:				// a receive for the first data byte.
		if ( charCountR > 1 ) {
			charCountR2--;						// make constant (byte count + 1) into byte count
			charCountR--;
			}
//		receivedByte = *VIA1_shift;					// read shift reg to turn off SR int
		PGE_ISR_state = kPMUrcvData_cmd;
		*VIA1_auxillaryControl &= 0xEF;					// set shift register to input
		if ( !(*VIA2_dataB & PMack) )
			if ( ![self WaitForAckHi] ) {
				return;						// make sure ACK is high
				}
		*VIA2_dataB &= ~PMreq;						// assert /REQ
//		*VIA1_interruptEnable = 0x84;					// enable SR interrupt
		return;								// next interrupt will be the first data character
		
								// We are reading the response in a command transaction, and
	case kPMUrcvData_cmd:					// a data byte has arrived
		*dataPointer++ = *VIA1_shift;					// read the data byte
		charCountR2--;
		if ( charCountR2 > 0 ) {					// is there more to read?
			if ( !(*VIA2_dataB & PMack) )
				if ( ![self WaitForAckHi] ) {
					return;					// yes, make sure ACK is high
					}
			*VIA2_dataB &= ~PMreq;					// assert /REQ
			return;							// next interrupt will be next data byte
			}
		if ( clientRequest->pmCallback != NULL ) {			// no, make the client callback
			clientRequest->pmCallback(clientRequest->pmId, clientRequest->pmRefNum, charCountR, clientRequest->pmRBuffer);
			}
		PGE_ISR_state = kPMUidle;						// set the state
		if ( !PMU_int_pending ) {						// is PMU now requesting service?
//		if ( !(*VIA1_interruptFlag & 0x10) ) {					// is PMU now requesting service? (ifCB1)
//			if ( queueHead == (PMUmachMessage*)0 ) {			// no, queue empty?
//				*VIA1_interruptEnable = 0x90;				// yes, enable PMU interrupts ( ifCB1 )
//				return;							// and we are completely idle
//				}
			[self CheckRequestQueue];					// no, start next queued transaction
			}
		else {
//			*VIA1_interruptFlag = 0x10;		// PMU wants service, acknowledge VIA interrupt ( ifCB1 )	
			PMU_int_pending = FALSE;
			*VIA1_auxillaryControl |= 0x1C;					// set shift register to output
			*VIA1_shift = kPMUreadINT;					// give it the command byte
			*VIA2_dataB &= ~PMreq;						// assert /REQ
//			*VIA1_interruptEnable = 0x84;					// enable SR interrupt
			PGE_ISR_state = kPMUreadLen_int;				// set the state
			dataPointer = &interruptState[0];				// set up read pointer for data bytes
			return;					// next interrupt is command byte transmission complete
			}
		return;

	case kPMUdone:						// this was the last xmt SR interrupt of a command transaction
//		receivedByte = *VIA1_shift;					// read shift reg to turn off SR int

		if ( clientRequest->pmFlag ) {					// does this command cause input?
               		PGE_ISR_state = kPMUidle;                               // yes, set the state
			adb_reading = TRUE;					// don't do callback now
//			who_to_call = clientRequest->pmCallback;		// do it after the read completes
//			theirId = clientRequest->pmId;
//			theirRefNum = clientRequest->pmRefNum;
			ns_timeout((func)timer_expired,(void *)port,adb_read_timeout,CALLOUT_PRI_SOFTINT0);	// start timer
			if ( !PMU_int_pending ) {					// is PMU now requesting service?
//			if ( !(*VIA1_interruptFlag & 0x10) ) {				// is PMU now requesting service? (ifCB1)
//				*VIA1_interruptEnable = 0x90;				// yes, enable PMU interrupts ( ifCB1 )
				return;							// and we are completely idle
				}
			else {
//				*VIA1_interruptFlag = 0x10;		// PMU wants service, acknowledge VIA interrupt ( ifCB1 )	
				PMU_int_pending = FALSE;
				*VIA1_auxillaryControl |= 0x1C;					// set shift register to output
				*VIA1_shift = kPMUreadINT;					// give it the command byte
				*VIA2_dataB &= ~PMreq;						// assert /REQ
//				*VIA1_interruptEnable = 0x84;					// enable SR interrupt
				PGE_ISR_state = kPMUreadLen_int;				// set the state
				dataPointer = &interruptState[0];				// set up read pointer for data bytes
				return;					// next interrupt is command byte transmission complete
				}
			}
										// not an adb read
		if ( clientRequest->pmCallback != NULL ) {				// Make the client callback
			clientRequest->pmCallback(clientRequest->pmId, clientRequest->pmRefNum, 0, NULL);
			}
		if ( !PMU_int_pending ) {						// is PMU now requesting service?
//		if ( !(*VIA1_interruptFlag & 0x10) ) {					// is PMU now requesting service? (ifCB1)
//			if ( queueHead == (PMUmachMessage*)0  ) {			// no, queue empty?
				PGE_ISR_state = kPMUidle;
//				*VIA1_interruptEnable = 0x90;				// yes, enable PMU interrupts ( ifCB1 )
//				return;							// and we are completely idle
//				}
			[self CheckRequestQueue];					// no, start next queued transaction
			}
		else {
			*VIA1_interruptFlag = 0x10;		// PMU wants service, acknowledge VIA interrupt ( ifCB1 )	
			PMU_int_pending = FALSE;
			*VIA1_auxillaryControl |= 0x1C;					// set shift register to output
			*VIA1_shift = kPMUreadINT;					// give it the command byte
			*VIA2_dataB &= ~PMreq;						// assert /REQ
//			*VIA1_interruptEnable = 0x84;					// enable SR interrupt
			PGE_ISR_state = kPMUreadLen_int;				// set the state
			dataPointer = &interruptState[0];				// set up read pointer for data bytes
			return;					// next interrupt is command byte transmission complete
			}
		return;
	}
return;
}

// ****************************************************************************
//	interruptOccurredAt
//	PGE has interrupted.  Send the ReadInt command to find out why.
//	When the command byte is sent, the Shift Register will interrupt.
//	If we are mid-transaction when we find out about the interrupt,
//	set a flag and find out why later.
//
// ****************************************************************************

- (void)interruptOccurredAt:(int)localInterrupt
{
if ( PGE_ISR_state != kPMUidle ) {
	PMU_int_pending = TRUE;
	return;
	}
if ( !debugging ) {
								// make sure ACK is high
//	*VIA1_interruptFlag = 0x10;				// acknowledge VIA interrupt ( ifCB1 )
//	*VIA1_interruptEnable = 0x10;				// and disable it entirely ( ifCB1 )
	while ( !(*VIA2_dataB & PMack) ) {
		}
	*VIA1_auxillaryControl |= 0x1C;				// set shift register to output
	*VIA1_shift = kPMUreadINT;				// give it the command byte
	*VIA2_dataB &= ~PMreq;					// assert /REQ
//	*VIA1_interruptEnable = 0x84;				// enable SR interrupt
	PGE_ISR_state = kPMUreadLen_int;			// set the state
	dataPointer = &interruptState[0];			// set up read pointer for data bytes
	return;							// return till character transmission completes
	}
else {
	PMURequest	getInterruptState;			// debug mode PMU interrupt handler

//	[self AcknowledgePMUInterrupt];				// turn off VIA interrupt
	*VIA1_interruptEnable = 0x04;				// disable SR interrupt
	
	getInterruptState.pmCommand = kPMUreadINT;		// find out cause of interrupt from PGE
	getInterruptState.pmFlag = FALSE;
	getInterruptState.pmSLength1 = 0;
	getInterruptState.pmSLength2 = 0;
	getInterruptState.pmRBuffer = &interruptState[0];
	getInterruptState.pmCallback = gotInterruptCause;
	getInterruptState.pmId = self;
	
	[self StartPMUTransmission:&getInterruptState];
	}
}

	
// ****************************************************************************
// gotInterruptCause
// 
// Called by the debug-mode PMU interrupt handler as the Callback function
// after sending the kPMUreadInt command and receiving its response
// ****************************************************************************
void gotInterruptCause(id PMUdriver, UInt32 unused, UInt32 length, UInt8 * data)
{
UInt8 interruptSource;

interruptSource = *data;
	
if ( interruptSource & kPMUADBint ) {
	[PMUdriver ADBinput: length: data];
	}
else {
	if ( interruptSource & kPMUbattInt ) {
		IOLog("battery PGE interrupt");
		}
	else {
		if ( interruptSource & kPMUoneSecInt ) {
			IOLog("one-second PGE interrupt");
			}
		else {
			if ( interruptSource & kPMUenvironmentInt ) {
				IOLog("environment interrupt");
				}
			else {
				if ( interruptSource & kPMUbrightnessInt ) {
					IOLog("brightness button PGE interrupt");
					}
				else {
					IOLog("machine-dependent PGE interrupt");
					}
				}
			}
		}
	}
}


// ****************************************************************************
// SendPMUByte
// ****************************************************************************
- (void)SendPMUByte:(UInt8)theByte
{
*VIA1_auxillaryControl |= 0x1C;         // set shift register to output
eieio();
*VIA1_shift = theByte;                  // give it the byte
eieio();
*VIA2_dataB &= ~PMreq;   		// assert /REQ
eieio();
if ( [self WaitForAckLo] ) {            // ack now low
        *VIA2_dataB |= PMreq;    	// deassert /REQ line
        eieio();
        if ( ! [self WaitForAckHi] ) {
                return;
                }
        }
else {
        *VIA2_dataB |= PMreq;		// deassert /REQ line
        eieio();
        return;
        }
return;
}


// ****************************************************************************
// ReadPMUByte
// ****************************************************************************
- (void)ReadPMUByte:(UInt8 *)theByte
{
*VIA1_auxillaryControl |= 0x0C;                 // set shift register to input
*VIA1_auxillaryControl &= ~0x10;
*theByte = *VIA1_shift;                         // read a byte to reset shift reg
eieio();
*VIA2_dataB &= ~PMreq;           		// assert /REQ
eieio();
if ( [self WaitForAckLo] ) {                    // ack now low
        *VIA2_dataB |= PMreq;    		// deassert /REQ line
        eieio();
        if ( [self WaitForAckHi] ) {            // wait for /ACK high
                *theByte = *VIA1_shift;         // got it, read the byte
                eieio();
                }
        else {
                return;
                }
        }
else {
        *VIA2_dataB |= PMreq;    		// deassert /REQ line
        eieio();
        return;
        }
return;
}


// ****************************************************************************
// WaitForAckLo
// ****************************************************************************
- (Boolean)WaitForAckLo
{
struct timeval startTime;
struct timeval currentTime;
ns_time_t x;

// wait up to 32 milliseconds for Ack signal from PG&E to go low

IOGetTimestamp(&x);
ns_time_to_timeval(x, &startTime);		        		// get current time

while ( TRUE ) {
        if ( !(*VIA2_dataB & PMack) ) {
                return ( TRUE );					// ack is low, return
                }
	IOGetTimestamp(&x);
        ns_time_to_timeval(x, &currentTime);
	if ( startTime.tv_usec > currentTime.tv_usec ) {
		currentTime.tv_usec += 1000000;				// clock has wrapped, adjust it
		}
	if ( currentTime.tv_usec > (startTime.tv_usec + 32000) ) {	// has 32 ms elapsed?
                return ( FALSE );					// yes, return
                }
        }
}


// ****************************************************************************
// WaitForAckHi
// ****************************************************************************
- (Boolean)WaitForAckHi
{
struct timeval startTime;
struct timeval currentTime;
ns_time_t x;

// wait up to 32 milliseconds for Ack signal from PG&E to go high

IOGetTimestamp(&x);
ns_time_to_timeval(x, &startTime);		        		// get current time

while ( TRUE ) {
	if ( *VIA2_dataB & PMack ) {
		return ( TRUE );					// ack is high, return
		}
	IOGetTimestamp(&x);
        ns_time_to_timeval(x, &currentTime);
        if ( startTime.tv_usec > currentTime.tv_usec ) {
                currentTime.tv_usec += 1000000;                         // clock has wrapped, adjust it
                }
        if ( currentTime.tv_usec > (startTime.tv_usec + 32000) ) {      // has 32 ms elapsed?
                return ( FALSE );                                       // yes, return
                }
        }
}


// ****************************************************************************
// GetPMUInterruptState
// ****************************************************************************
- (UInt8)GetPMUInterruptState
{				// return current state of CB1 int enable
return (*VIA1_interruptEnable & (1<<ifCB1));
}


// ****************************************************************************
// RestorePMUInterrupt
// ****************************************************************************
- (void)RestorePMUInterrupt:(UInt8)savedValue
{
if ( savedValue ) {		// restore VIA interrupt state
	*VIA1_interruptEnable = savedValue | 0x80;
	}
eieio();
}


// ****************************************************************************
// DisablePMUInterrupt
// ****************************************************************************
- (void)DisablePMUInterrupt
{
*VIA1_interruptEnable = 1<<ifCB1;
eieio();
}


// ****************************************************************************
// EnablePMUInterrupt
// ****************************************************************************
- (void)EnablePMUInterrupt
{
*VIA1_interruptEnable = (1<<ifCB1) | 0x80;
eieio();
}


// ****************************************************************************
// AcknowledgePMUInterrupt
// ****************************************************************************
- (void)AcknowledgePMUInterrupt
{
*VIA1_interruptFlag = 1<<ifCB1;
eieio();
}


// ****************************************************************************
// GetSRInterruptState
// ****************************************************************************
- (UInt8)GetSRInterruptState
{					// return current state of SR int enable
return (*VIA1_interruptEnable & (1<<ifSR));
}


// ****************************************************************************
// RestoreSRInterrupt
// ****************************************************************************
- (void)RestoreSRInterrupt:(UInt8)savedValue
{
if ( savedValue ) {			// restore SR interrupt state
	*VIA1_interruptEnable = savedValue | 0x80;
	eieio();
	}
}


// ****************************************************************************
// DisableSRInterrupt
// ****************************************************************************
- (void)DisableSRInterrupt
{
*VIA1_interruptEnable = 1<<ifSR;
}


// ****************************************************************************
// EnableSRInterrupt
// ****************************************************************************
- (void)EnableSRInterrupt
{
*VIA1_interruptEnable = (1<<ifSR) | 0x80;
}



// ****************************************************************************
// timer_expired
//
// Our adb-read timer has expired, so we have to notify our i/o thread by
// enqueuing a Timeout message to its interrupt port.
// ****************************************************************************
void timer_expired(port_t mach_port)
{
PMUmachMessage	request;

request.msgHeader.msg_simple = TRUE;
request.msgHeader.msg_type = MSG_TYPE_NORMAL;
request.msgHeader.msg_id = IO_TIMEOUT_MSG;
request.msgHeader.msg_remote_port = mach_port;
request.msgHeader.msg_local_port = PORT_NULL;
request.msgHeader.msg_size = sizeof(msg_header_t);
msg_send_from_kernel(&request.msgHeader, MSG_OPTION_NONE, 0);

}


@end