/* $OpenBSD: pm_direct.c,v 1.8 2005/02/11 20:09:30 martin Exp $ */ /* $NetBSD: pm_direct.c,v 1.4 1998/02/23 03:11:26 scottr Exp $ */ /* * Copyright (C) 1997 Takashi Hamada * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Takashi HAMADA * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* #define PM_DEBUG 1 */ /* #define PM_GRAB_SI 1 */ #include #include #include #include #include #include #include #include #include #include #include "pm_direct.h" /* hardware dependent values */ extern u_short ADBDelay; extern u_int32_t HwCfgFlags3; /* define the types of the Power Manager */ #define PM_HW_UNKNOWN 0x00 /* don't know */ #define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */ #define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */ /* useful macros */ #define PM_SR() via_reg(VIA1, vSR) #define PM_VIA_INTR_ENABLE() via_reg(VIA1, vIER) = 0x90 #define PM_VIA_INTR_DISABLE() via_reg(VIA1, vIER) = 0x10 #define PM_VIA_CLR_INTR() via_reg(VIA1, vIFR) = 0x90 #define PM_SET_STATE_ACKON() via_reg(VIA2, vBufB) |= 0x04 #define PM_SET_STATE_ACKOFF() via_reg(VIA2, vBufB) &= ~0x04 #define PM_IS_ON ( 0x02 == (via_reg(VIA2, vBufB) & 0x02) ) #define PM_IS_OFF ( 0x00 == (via_reg(VIA2, vBufB) & 0x02) ) /* * Variables for internal use */ int pmHardware = PM_HW_UNKNOWN; u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */ u_int pm_LCD_brightness = 0x0; u_int pm_LCD_contrast = 0x0; u_int pm_counter = 0; /* clock count */ /* these values shows that number of data returned after 'send' cmd is sent */ char pm_send_cmd_type[] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x01,0x01,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00, 0xff,0x00,0x02,0x01,0x01,0xff,0xff,0xff, 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x04,0x14,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x02,0xff,0xff,0xff,0xff,0xff, 0x01,0x01,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, 0x01,0x00,0x02,0x02,0xff,0x01,0x03,0x01, 0x00,0x01,0x00,0x00,0x00,0xff,0xff,0xff, 0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, 0x01,0x01,0x01,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0xff,0xff,0xff,0xff,0x04,0x04, 0x04,0xff,0x00,0xff,0xff,0xff,0xff,0xff, 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x01,0x02,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, 0x02,0x02,0x02,0x04,0xff,0x00,0xff,0xff, 0x01,0x01,0x03,0x02,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x01,0x01,0xff,0xff,0x00,0x00,0xff,0xff, 0xff,0x04,0x00,0xff,0xff,0xff,0xff,0xff, 0x03,0xff,0x00,0xff,0x00,0xff,0xff,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; /* these values shows that number of data returned after 'receive' cmd is sent */ char pm_receive_cmd_type[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x02,0xff,0xff,0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x05,0x15,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x02,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0x03,0x03,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x04,0x04,0x03,0x09,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x01, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x06,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x02,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0x00,0x00,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x02,0xff,0xff,0x02,0xff,0xff,0xff, 0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0x02,0xff,0xff,0xff,0xff,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, }; /* * Define the private functions */ /* for debugging */ #ifdef PM_DEBUG void pm_printerr( char *, int, int, char * ); #endif int pm_wait_busy(int); int pm_wait_free(int); /* these functions are for the PB1XX series */ int pm_receive_pm1(u_char *); int pm_send_pm1(u_char,int); int pm_pmgrop_pm1(PMData *); void pm_intr_pm1(void); /* these functions are for the PB Duo series and the PB 5XX series */ int pm_receive_pm2(u_char *); int pm_send_pm2(u_char); int pm_pmgrop_pm2(PMData *); void pm_intr_pm2(void); /* this function is MRG-Based (for testing) */ int pm_pmgrop_mrg(PMData *); /* these functions are called from adb_direct.c */ void pm_setup_adb(void); void pm_check_adb_devices(int); void pm_intr(void); int pm_adb_op(u_char *, void *, void *, int); /* these functions also use the valiables of adb_direct.c */ void pm_adb_get_TALK_result(PMData *); void pm_adb_get_ADB_data(PMData *); void pm_adb_poll_next_device_pm1(PMData *); /* * These valiables are in adb_direct.c. */ extern u_char *adbBuffer; /* pointer to user data area */ #define MAX_ADB_MSG_LENGTH 20 extern u_char adbInputBuffer[MAX_ADB_MSG_LENGTH]; /* data input buffer */ extern void *adbCompRout; /* pointer to the completion routine */ extern void *adbCompData; /* pointer to the completion routine data */ extern int adbWaiting; /* waiting for return data from the device */ extern int adbWaitingCmd; /* ADB command we are waiting for */ extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ /* * Define the external functions */ extern void adb_comp_exec(void); /* from adb_direct.c */ #ifdef PM_DEBUG /* * This function dumps contents of the PMData */ void pm_printerr(ttl, rval, num, data) char *ttl; int rval; int num; char *data; { int i; printf( "pm: %s:%04x %02x ", ttl, rval, num ); for( i=0; icommand ) { default: for( i=0; i<7; i++ ) { via_reg(VIA2, vDirA) = 0x00; /* wait until PM is free */ if (pm_wait_free( ADBDelay ) == 0) { /* timeout */ via_reg(VIA2, vDirA) = 0x00; /* restore formar value */ via_reg(VIA1, vDirA) = via1_vDirA; via_reg(VIA1, vIER) = via1_vIER; return( 0xffffcd38 ); } switch( mac68k_machine.machineid ) { case MACH_MACPB160: case MACH_MACPB165: case MACH_MACPB165C: case MACH_MACPB180: case MACH_MACPB180C: { int delay = ADBDelay * 16; via_reg(VIA2, vDirA) = 0x00; while((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0)) delay--; if (delay < 0) { /* timeout */ via_reg(VIA2, vDirA) = 0x00; /* restore formar value */ via_reg(VIA1, vIER) = via1_vIER; return( 0xffffcd38 ); } } } /* end switch */ s=splhigh(); via1_vDirA = via_reg(VIA1, vDirA); via_reg(VIA1, vDirA) &= 0x7f; pm_cmd = (u_char)(pmdata->command & 0xff); if ((rval = pm_send_pm1( pm_cmd, ADBDelay*8 )) == 0) /* succeeded to send PM command */ break; via_reg(VIA1, vDirA) = via1_vDirA; splx(s); } /* end for */ /* failed to send a command */ if (i == 7) { via_reg(VIA2, vDirA) = 0x00; /* restore formar value */ via_reg(VIA1, vDirA) = via1_vDirA; via_reg(VIA1, vIER) = via1_vIER; return( 0xffffcd38 ); } /* send # of PM data */ num_pm_data = pmdata->num_data; if ((rval = pm_send_pm1( (u_char)(num_pm_data & 0xff), ADBDelay*8 )) != 0) break; /* timeout */ /* send PM data */ pm_buf = (u_char *)pmdata->s_buf; for( i=0; icommand = pm_data; /* receive number of PM data */ if ((rval = pm_receive_pm1( &pm_data )) != 0) break; /* timeout */ num_pm_data = pm_data; pmdata->num_data = num_pm_data; /* receive PM data */ pm_buf = (u_char *)pmdata->r_buf; for( i=0; icommand ) { default: /* wait until PM is free */ pm_cmd = (u_char)(pmdata->command & 0xff); rval = 0xcd38; if (pm_wait_free( ADBDelay * 4 ) == 0) break; /* timeout */ if (HwCfgFlags3 & 0x00200000) { /* PB 160, PB 165(c), PB 180(c) ? */ int delay = ADBDelay * 16; via_reg(VIA2, vDirA) = 0x00; while((via_reg(VIA2, 0x200) == 0x07) && (delay >= 0)) delay--; if (delay < 0) { rval = 0xffffcd38; break; /* timeout */ } } /* send PM command */ if ((rval = pm_send_pm2( (u_char)(pm_cmd & 0xff) ))) break; /* timeout */ /* send number of PM data */ num_pm_data = pmdata->num_data; if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ if (pm_send_cmd_type[pm_cmd] < 0) { if ((rval = pm_send_pm2( (u_char)(num_pm_data & 0xff) )) != 0) break; /* timeout */ pmdata->command = 0; } } else { /* PB 1XX series ? */ if ((rval = pm_send_pm2( (u_char)(num_pm_data & 0xff) )) != 0) break; /* timeout */ } /* send PM data */ pm_buf = (u_char *)pmdata->s_buf; for( i=0; inum_data = pm_num_rx_data; if (pm_num_rx_data == 0) { rval = 0; break; /* no return data */ } /* receive PM command */ pm_data = pmdata->command; if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ pm_num_rx_data--; if (pm_num_rx_data == 0) if ((rval = pm_receive_pm2( &pm_data )) != 0) { rval = 0xffffcd37; break; } pmdata->command = pm_data; } else { /* PB 1XX series ? */ if ((rval = pm_receive_pm2( &pm_data )) != 0) { rval = 0xffffcd37; break; } pmdata->command = pm_data; } /* receive number of PM data */ if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ if (pm_num_rx_data < 0) { if ((rval = pm_receive_pm2( &pm_data )) != 0) break; /* timeout */ num_pm_data = pm_data; } else num_pm_data = pm_num_rx_data; pmdata->num_data = num_pm_data; } else { /* PB 1XX serias ? */ if ((rval = pm_receive_pm2( &pm_data )) != 0) break; /* timeout */ num_pm_data = pm_data; pmdata->num_data = num_pm_data; } /* receive PM data */ pm_buf = (u_char *)pmdata->r_buf; for( i=0; i 0x5a) pm_LCD_brightness = 0x7f; pmdata.data[0] = pm_LCD_brightness; rval = pm_pmgrop_pm2( &pmdata ); break; } /* ADB data that were requested by TALK command */ case 0x10: case 0x14: pm_adb_get_TALK_result(&pmdata); break; /* ADB device event */ case 0x16: case 0x18: case 0x1e: pm_adb_get_ADB_data(&pmdata); break; default: { #ifdef PM_DEBUG pm_printerr( "driver does not supported this event.", pmdata.data[2], pmdata.num_data, pmdata.data ); #endif } break; } splx(s); } /* * MRG-based PMgrOp routine */ int pm_pmgrop_mrg(pmdata) PMData *pmdata; { u_int32_t rval=0; asm(" movl %1, a0 .word 0xa085 movl d0, %0" : "=g" (rval) : "g" (pmdata) : "a0", "d0" ); return rval; } /* * My PMgrOp routine */ int pmgrop(pmdata) PMData *pmdata; { switch( pmHardware ) { case PM_HW_PB1XX: { return( pm_pmgrop_pm1(pmdata) ); break; } case PM_HW_PB5XX: { return( pm_pmgrop_pm2(pmdata) ); break; } default: /* return( pmgrop_mrg(pmdata) ); */ return( -1 ); } } /* * My PM interrupt routine */ void pm_intr(void) { switch( pmHardware ) { case PM_HW_PB1XX: { pm_intr_pm1(); break; } case PM_HW_PB5XX: { pm_intr_pm2(); break; } default: break; } } /* * Synchronous ADBOp routine for the Power Manager */ int pm_adb_op(buffer, compRout, data, command) u_char *buffer; void *compRout; void *data; int command; { int i,len; int s; int rval; int delay; PMData pmdata; if (adbWaiting == 1) return( -1 ); s = splhigh(); via_reg(VIA1, vIER) = 0x10; adbBuffer = buffer; adbCompRout = compRout; adbCompData = data; pmdata.command = 0x20; pmdata.s_buf = pmdata.data; pmdata.r_buf = pmdata.data; if ((command & 0xc) == 0x8) { /* if the command is LISTEN, add number of ADB data to number of PM data */ if (buffer != (u_char *)0) pmdata.num_data = buffer[0] + 3; } else { pmdata.num_data = 3; } pmdata.data[0] = (u_char)(command & 0xff); pmdata.data[1] = 0; if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { pmdata.data[2] = buffer[0]; /* number of data */ for( i=0; idata[3] & 0xff; pmdata->data[2] &= 0xf; pmdata->data[1] = pmdata->data[3]; pmdata->data[3] = pmdata->num_data - 2; adbInputBuffer[0] = pmdata->num_data + 1; for( i=1; inum_data+2; i++ ) adbInputBuffer[i] = pmdata->data[i]; if ((adbWaiting == 1) && (rx_pm_adb_cmd == adbWaitingCmd)) { if (adbStarting == 0) adb_complete( &pmdata->data[3] , (long)0, adbWaitingCmd ); adbWaitingCmd = 0x0; adbWaiting = 0; adb_comp_exec(); adbBuffer = (long)0; adbCompRout = (long)0; adbCompData = (long)0; } } void pm_adb_get_ADB_data(pmdata) PMData *pmdata; { int i; i = (u_int)pmdata->data[3] & 0xff; pmdata->data[2] &= 0xf; pmdata->data[1] = pmdata->data[3]; pmdata->data[3] = pmdata->num_data - 2; adbInputBuffer[0] = pmdata->num_data + 1; if (adbStarting == 0) adb_complete( &pmdata->data[3] , (long)0, i ); } void pm_adb_poll_next_device_pm1(pmdata) PMData *pmdata; { int i; int ndid; u_short bendid = 0x1; int rval; PMData tmp_pmdata; /* find another existent ADB device to poll */ for( i=1; i<16; i++ ) { ndid = (((pmdata->data[3] & 0xf0) >> 4) + i) & 0xf; bendid <<= ndid; if ((pm_existent_ADB_devices & bendid) != 0) break; } /* poll the other device */ tmp_pmdata.command = 0x20; tmp_pmdata.num_data = 3; tmp_pmdata.s_buf = tmp_pmdata.data; tmp_pmdata.r_buf = tmp_pmdata.data; tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ tmp_pmdata.data[2] = 0x00; rval = pmgrop( &tmp_pmdata ); }