#define UwsAsr_c


// **********************************************************************
// * uwsasr.c                                                           *
// *                                                                    *
// * Written by Matthew DeLoera (deloera@us.ibm.com)                    *
// * (C) Copyright IBM Corporation, 1998-2001                           *
// *                                                                    *
// *  This software may be used and distributed according to the terms  *
// *  of the GNU Public License, incorporated herein by reference.      *
// **********************************************************************

// **********************************************************************
// * uwsasr.c - This source file provides all Automatic Server Restart  *
// *            functionality.                                          *
// *                                                                    *
// * Exported Functions                                                 *
// * ------------------                                                 *
// * sysSpASR         - enables ASR hardware and starts periodic ping   *
// * sysSpASRshutdown - kills periodic ping and disables ASR hardware   *
// *                                                                    *
// * Changes                                                            *
// * -------                                                            *
// * 01/29/2001 - Cleaned up for open source release.                   *
// * ----------------------------------------------------------------   *
// * 06/27/2002 - Added multi node support       // PHR_MULTI_4         *
// **********************************************************************


// ************
// * INCLUDES *
// ************

#include "uwscmd.h"


// ********************
// * LOCAL PROTOTYPES *
// ********************
int  ASRtestcounter = 0;
int  sysSpASR(void);
void sysSpASRshutdown(void);
void ASRtimeout(void);                              // PHR_MULTI
void ToggleAsrBit(void);
void ASRenable(void);
void ASRdisable(void);
void ASRbaseaddress(void);


// ************************************************************************************
// * sysSpASR - This routine locates and enables the ASR hardware. It also prepares a *
// *            timer task to periodically toggle the ASR hardware. If it is unable   *
// *            to locate the ASR hardware, it fails.                                 *
// *                                                                                  *
// * Returns:                                                                         *
// *      0       - successful                                                        *
// *      ENODEV  - no such device                                                    *
// ************************************************************************************
int sysSpASR()
{
   // ANNOUNCE TEST MODE IF ENABLED
   if (DBG_ASR_TEST)
   {
      printk(KERN_CRIT "DBG_ASR_TEST: ASR test mode enabled - ASR will halt after %d timeouts.\n", DBG_ASR_TEST);
   }

   // INITIALIZE THE ASR TIMER FOR A 10s INTERVAL
   init_timer(&(pSpPrimaryNode->ASRtimer));
   pSpPrimaryNode->ASRtimer.function = (void *)ASRtimeout;
   pSpPrimaryNode->ASRtimer.expires  = jiffies + (ULONG)ASR_TIMEOUT * HZ;   // PHR_MULTI

   if (DBG_ASR) 
   {
      printk(KERN_CRIT "DBG_ASR: ASR timer has been initialized.\n");
   }
   
   // SET ASR BASE ADDRESS IN DRIVER EXTENSION
   ASRbaseaddress();

   // TOGGLE THE ASR BIT PRIOR TO ENABLEING THE ASR H/W
   ToggleAsrBit();

   // ENABLE ASR
   ASRenable();
  
   // START THE ASR PERIODIC PING PROCESS
   if (DBG_ASR)
   {
       printk(KERN_CRIT "DBG_ASR: Starting the ASR timer.\n");
   }

   add_timer(&(pSpPrimaryNode->ASRtimer));

   return 0;
}


// ************************************************************************************
// * sysSpASRshutdown - This routine deletes the ASR timer and disables the hardware. *
// ************************************************************************************
void sysSpASRshutdown()
{
    // STOP THE ASR PERIODIC PING PROCESS
   if (DBG_ASR)
   {
       printk(KERN_CRIT "DBG_ASR: Deleting the ASR timer.\n");
   }

   del_timer(&(pSpPrimaryNode->ASRtimer));

   ASRdisable();
   return;
}


// ****************************************************************************
// * ASRTimeout - This routine is called each time the ASR timer process      *
// * (MMD00)      pings. It toggles the ASR hardware, renews the ASR timer,   *
// *              and exits.                                                  *
// ****************************************************************************
void ASRtimeout(void)           // PHR_MULTI
{
     // IF DRIVER IS UNLOADING, REJECT IOCTL REQUESTS
     if ( ShuttingDown )
     {
        if (DBG_ASR)
        {
           printk(KERN_CRIT "DBG_ASR: Driver currently unloading. Exiting ASRtimeout().\n");
        }

        return;
     }

     // TOGGLE ASR HARDWARE
     if ( !DBG_ASR_TEST )
     {
        ToggleAsrBit();
     }
     else if ( ASRtestcounter < DBG_ASR_TEST )
     {
        ToggleAsrBit();
        ASRtestcounter++;

        if ( (DBG_ASR_TEST - ASRtestcounter) > 0 )
        {
           if ( (DBG_ASR_TEST - ASRtestcounter) > 1 )
           {
              printk(KERN_CRIT "DBG_ASR_TEST: There are %i ASR toggles remaining before system reboot.\n", 
                    (DBG_ASR_TEST - ASRtestcounter));
           }
           else
           {
              printk(KERN_CRIT "DBG_ASR_TEST: There is 1 ASR toggle remianing before system reboot.\n");
           }
        }
        else
        {
           printk(KERN_CRIT "DBG_ASR_TEST: ASR WatchDog bit will not be toggled anymore.\n"); 
           printk(KERN_CRIT "DBG_ASR_TEST: ASR reboot will occur in 256 seconds\n");
        }
     }

     // REFRESH THE ASR TIMER
     pSpPrimaryNode->ASRtimer.function = (void *)ASRtimeout;
     pSpPrimaryNode->ASRtimer.expires  = jiffies + (ULONG)ASR_TIMEOUT * HZ;
     add_timer(&(pSpPrimaryNode->ASRtimer));
     return;
}


// **************************************************************************
// * ToggleAsrBit - This routine toggles the ASR hardware.                  * 
// *                                                                        *
// * Change History                                                         *
// *                  PHR_191379 - Added ASR support for Jasper and Pearl   *
// **************************************************************************
void ToggleAsrBit()
{
   if (pSpPrimaryNode->AsmType == ASMTYPE_ASR)
   { 
      UCHAR reg; 
      USHORT addr;
           
      addr = pSpPrimaryNode->ASRbaseaddress + 0x04;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR(addr);

      // TOGGLE THE ASR_WDog_Toggle LINE (BIT 6) FROM 0-1-0
      WRITE_PORT_UCHAR(addr, (reg & ~DEF_ASR_TOGGLE)); // write 0 to toggle bit
      reg = READ_PORT_UCHAR(addr);

      WRITE_PORT_UCHAR(addr, (reg |  DEF_ASR_TOGGLE)); // write 1 to toggle bit 
      reg = READ_PORT_UCHAR(addr);

      WRITE_PORT_UCHAR(addr, (reg & ~DEF_ASR_TOGGLE)); // write 0 to toggle bit 
      reg = READ_PORT_UCHAR(addr);

      if (DBG_ASR)
      {
         printk(KERN_CRIT "DBG_ASR: Toggled the ASR WatchDog bit (6) from 0-1-0.\n"); 
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_PEARL )
   {
      // Reset Pearl's ASR HW timer by toggling the ASR WatchDog bit from 0-1-0
      UCHAR reg; 

      // READ THE INITIAL GPIO26 REGISTER VALUE
      reg = READ_PORT_UCHAR( PEARL_READ );
      
      WRITE_PORT_UCHAR(PEARL_WRITE, (reg & ~PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR( PEARL_READ );

      WRITE_PORT_UCHAR(PEARL_WRITE, (reg |  PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR( PEARL_READ );

      WRITE_PORT_UCHAR(PEARL_WRITE, (reg & ~PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR( PEARL_READ );

      if (DBG_ASR)
      {
         printk(KERN_CRIT "DBG_ASR: Toggled Pearls ASR WatchDog bit from 0-1-0.\n"); 
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_JASPER  )
   {
       // Reset Jasper's ASR HW timer by toggling the ASR_WDog_Toggle signal (bit 1) from 0-1-0
      USHORT jasperAddr; 
      UCHAR  reg; 

      jasperAddr= pSpPrimaryNode->ASRbaseaddress + JASPER_ASR_REG_OFFSET;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR( jasperAddr );
      
      WRITE_PORT_UCHAR(jasperAddr, (reg & ~JASPER_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      WRITE_PORT_UCHAR(jasperAddr, (reg |  JASPER_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      WRITE_PORT_UCHAR(jasperAddr, (reg & ~JASPER_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      if (DBG_ASR)
      {
         printk(KERN_CRIT "DBG_ASR: Toggled Jaspers ASR WatchDog bit from 0-1-0.\n"); 
      }
   }

   return;
}

// **************************************************************************
// * ASRenable - MMD00 - This routine enables ASR on all ASR systems        *
// *                                                                        *
// * Change History                                                         *
// *                  PHR_191379 - Added ASR support for Jasper and Pearl   *
// **************************************************************************
void ASRenable()
{
   if ( pSpPrimaryNode->AsmType == ASMTYPE_ASR )
   {
      USHORT addr; 
      UCHAR  reg;

      addr = pSpPrimaryNode->ASRbaseaddress + 0x04;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR(addr);

      // CLEAR BITS 6 & 7 OF THE ASR_WDog_Toggle
      WRITE_PORT_UCHAR(addr, (reg & ~(DEF_ASR_TOGGLE | DEF_ASR_DISABLE)));

      // DEBUG AND EXIT
      reg = READ_PORT_UCHAR(addr);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "DBG_ASR: Enabled ASR @ addr 0x%04x with contents %02x.\n", addr, reg);
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_PEARL )
   {
      UCHAR reg; 

      // READ THE INITIAL GPIO26 REGISTER VALUE
      reg = READ_PORT_UCHAR(PEARL_READ);

      // First make sure Pearl's ASR hardware timer is reset by toggling its 
      // ASR hardware timer signal from 0-1-0.
      WRITE_PORT_UCHAR(PEARL_WRITE, (reg & ~PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);

      WRITE_PORT_UCHAR(PEARL_WRITE, (reg | PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);

      WRITE_PORT_UCHAR(PEARL_WRITE, (reg & ~PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);

      if (DBG_ASR)
      {
         printk(KERN_CRIT "DBG_ASR: Toggled Pearls ASR WatchDog bit from 0-1-0 prior to enabling ASR.\n"); 
      }
      
      // Now enable Pearl's ASR hardware circuit. 
      WRITE_PORT_UCHAR(PEARL_WRITE, (reg & ~PEARL_ASR_DISABLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "ASR_DBG: Enabled Pearls ASR @ addr 0x%04x with contents %02x.\n", PEARL_WRITE, reg);
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_JASPER )
   {
      USHORT jasperAddr;
      UCHAR  reg;

      jasperAddr = pSpPrimaryNode->ASRbaseaddress + JASPER_ASR_REG_OFFSET;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR(jasperAddr);

      // First make sure the hardware timer is reset by toggling Jasper's 
      // ASR hardware timer line.
      WRITE_PORT_UCHAR(jasperAddr, (reg & (~JASPER_ASR_TOGGLE_MASK)));
      reg = READ_PORT_UCHAR(jasperAddr);

      WRITE_PORT_UCHAR(jasperAddr, (reg | JASPER_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      WRITE_PORT_UCHAR(jasperAddr, (reg & (~JASPER_ASR_TOGGLE_MASK)));
      reg = READ_PORT_UCHAR(jasperAddr);

      if (DBG_ASR)
      {
         printk(KERN_CRIT "DBG_ASR: Toggled Jaspers ASR WatchDog bit from 0-1-0 prior to enabling ASR.\n"); 
      }
      
      // Now enable Jasper's ASR hardware circuit.    // DEBUG AND EXIT 
      WRITE_PORT_UCHAR(jasperAddr, (reg & (~JASPER_ASR_DISABLE_MASK)));
      reg = READ_PORT_UCHAR(jasperAddr);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "ASR_DBG: Enabled Jaspers ASR @ addr 0x%04x with contents %02x.\n", jasperAddr, reg);
      }
   }

   return;
}

// **************************************************************************
// * ASRdisable - MMD00 - This routine disables ASR on all ASR systems      *
// *                                                                        *
// * Change History                                                         *
// *                  PHR_191379 - Added ASR support for Jasper and Pearl   *
// **************************************************************************
void ASRdisable()
{
   if (pSpPrimaryNode->AsmType == ASMTYPE_ASR)
   {
      USHORT addr;
      UCHAR  reg;

      addr = pSpPrimaryNode->ASRbaseaddress + 0x04;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR(addr);

      // SET BITS 6 & 7 OF THE ASR_WDOG_TOGGLE
      WRITE_PORT_UCHAR(addr, (reg | DEF_ASR_TOGGLE | DEF_ASR_DISABLE));
      reg = READ_PORT_UCHAR(addr);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "ASR_DBG: Disabled ASR @ addr 0x%04x with contents %02x.\n", addr, reg);
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_PEARL )
   {
      UCHAR reg; 

      // READ THE INITIAL GPIO13 REGISTER VALUE
      reg = READ_PORT_UCHAR(PEARL_READ);

      // SET TOGGLE MASK HIGH
      WRITE_PORT_UCHAR(PEARL_WRITE, (reg | PEARL_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);
      
      // DISABLE PEARL'S ASR HARDWARE CIRCUIT. 
      WRITE_PORT_UCHAR(PEARL_WRITE, (reg | PEARL_ASR_DISABLE_MASK));
      reg = READ_PORT_UCHAR(PEARL_READ);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "ASR_DBG: Disabled Pearls ASR @ addr 0x%04x with contents %02x.\n", PEARL_WRITE, reg);
      }
   }
   else if( pSpPrimaryNode->AsmType == ASMTYPE_JASPER )
   {
      USHORT jasperAddr; 
      UCHAR  reg; 

      jasperAddr = pSpPrimaryNode->ASRbaseaddress + JASPER_ASR_REG_OFFSET;

      // READ THE INITIAL REGISTER VALUE
      reg = READ_PORT_UCHAR(jasperAddr);

      // SET TOGGLE MASK HIGH
      WRITE_PORT_UCHAR(jasperAddr, (reg | JASPER_ASR_TOGGLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      // DISABLE JASPER'S ASR HARDWARE CIRCUIT.
      WRITE_PORT_UCHAR(jasperAddr, (reg | JASPER_ASR_DISABLE_MASK));
      reg = READ_PORT_UCHAR(jasperAddr);

      if (DBG_ASR)
      {
        printk(KERN_CRIT "ASR_DBG: Disabled Jaspers ASR @ addr 0x%04x with contents %02x.\n", jasperAddr, reg);
      }
   }

   return;
}


// **************************************************************************
// * ASRbaseaddress - MMD00 - This routine queries system BIOS to determine *
// *                          the base address of the ASR hardware.         *
// *                                                                        *
// * Change History                                                         *
// *                  PHR_191379 - Added ASR support for Jasper and Pearl   *
// **************************************************************************
void ASRbaseaddress()
{
  if (pSpPrimaryNode->AsmType == ASMTYPE_ASR)
  {
     UCHAR  low, high;

     // SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F)
     WRITE_PORT_UCHAR(0x2E, 0x07);
     WRITE_PORT_UCHAR(0x2F, 0x07);
  
     // SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS
     WRITE_PORT_UCHAR(0x2E, 0x60);
     high = READ_PORT_UCHAR(0x2F);

     // SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS
     WRITE_PORT_UCHAR(0x2E, 0x61);
     low = READ_PORT_UCHAR(0x2F);

     // COMBINE AND RETURN BASE ADDRESS
     pSpPrimaryNode->ASRbaseaddress = (USHORT)high * 0x100;
     pSpPrimaryNode->ASRbaseaddress = pSpPrimaryNode->ASRbaseaddress | (USHORT)low;
  }
  else if (pSpPrimaryNode->AsmType == ASMTYPE_JASPER)
  {
     // Select the SuperIO chip in the PCI I/O port register 
     WRITE_PORT_ULONG(0x0CF8, 0x8000F858);

     // Read the base address for the SuperIO chip.  Only the lower 16 bits are valid, but the 
     // address is word aligned so the last bit must be masked off.
     pSpPrimaryNode->ASRbaseaddress = (unsigned short)(READ_PORT_ULONG(0x0CFC) & 0xFFFE);
  }
  else if (pSpPrimaryNode->AsmType == ASMTYPE_PEARL)
  {
      pSpPrimaryNode->ASRbaseaddress = 0x0E04;
  }

  if (DBG_ASR)
  {
     printk(KERN_CRIT "DBG_ASR: ASR base address = %x.\n", pSpPrimaryNode->ASRbaseaddress);
  }

  return;
}

#undef UwsAsr_c


