/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "%w% /%e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = S506PIIX.C
 *
 * DESCRIPTIVE NAME = IBM1S506.ADD - Adapter Driver for ST506/IDE DASD
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Adapter Driver PIIX routines.
 *
 * Purpose:
 *
 *
 *
 *
 *
*/
 #define INCL_NOPMAPI
 #define INCL_DOSINFOSEG                                            /*@V153620*/
 #define INCL_NO_SCB
 #define INCL_INITRP_ONLY
 #define INCL_DOSERRORS
 #include "os2.h"
 #include "dos.h"
 #include "bseerr.h"
 #include "dskinit.h"
 #include <scb.h>
 #include <abios.h>

 #include "devhdr.h"
 #include "iorb.h"
 #include "reqpkt.h"
 #include "dhcalls.h"
 #include "addcalls.h"
 #include "devclass.h"

 #include "s506cons.h"
 #include "s506type.h"
 #include "s506regs.h"
 #include "s506ext.h"
 #include "s506pro.h"
 #include "s506misc.h"
 #include "s506ps2.h"

/*--------------------------------------------*/
/*                                            */
/* SetupPCIChips()                            */
/*                                            */
/*                                            */
/*--------------------------------------------*/
VOID SetupPCIChips( VOID )                                          /*@V182487*/
{
   USHORT i, j;
   USHORT piix_Level = 0;
   ULONG  PhysAddr;
   NPATBL npAT;                                                     
   NPACB  npACB;                                                    

   for (i=0,npAT = AdapterTable; i < MAX_ADAPTERS ; i++,npAT++ )    
   {
     npACB = npAT->npACB;                /* ptr to next Adapter Control Block */

     /*                                                                @V159935
     ** If there were no units attached, then the ACB has been         @V159935
     ** deallocated, so do no configuration on this IDE channel.       @V159935
     */                                                              /*@V159935*/

     if( (!npACB) || (npACB->cUnits == 0) )
        continue;                                                    /*@V159935*/

     if ( npAT->Flags & ATBF_BM_DMA )    /* if Bus Master DMA on this adapter */
     {
       /*-------------------------------------------*/           /*@V182487*/
       /* Get the proper PIIX level for the channel */           /*VVVVVVVV*/
       /*-------------------------------------------*/
       switch( npAT->PCIInfo.Ident.Device )
       {
          case PIIX_PCIIDE_DEV_ID:
               piix_Level = PIIX;
               break;
          case PIIX3_PCIIDE_DEV_ID:
               piix_Level = PIIX3;
               break;
          case PIIX4_PCIIDE_DEV_ID:
               piix_Level = PIIX4;
               break;
          case I82801AA_PCIIDE_DEV_ID:                            
               piix_Level = I82801AA;
               break;
          case I82801AB_PCIIDE_DEV_ID:                            
               piix_Level = I82801AB;
               break;
          case I82801BA1_PCIIDE_DEV_ID:                            
               piix_Level = I82801BA_1;
               break;
          case I82801BA2_PCIIDE_DEV_ID:                            
               piix_Level = I82801BA_2;
               break;
          case I82801BA3_PCIIDE_DEV_ID:                            
               piix_Level = I82801BA_3;
               break;
          case I82801BA4_PCIIDE_DEV_ID:                            
               piix_Level = I82801BA_4;
               break;
          case I82801BA5_PCIIDE_DEV_ID:                            
               piix_Level = I82801BA_5;
               break;
          case CMD646_PCIIDE_DEV_ID:                              
               piix_Level = CMD646;
               break;
          case CMD648_PCIIDE_DEV_ID:                              /*@VXXXXXX*/
               piix_Level = CMD648;
               break;
          case VIA596_PCIIDE_DEV_ID:                              /*V@253487*/
               piix_Level = VIA586;
               break;
          case SIS630_PCIIDE_DEV_ID:                              /*V@253487*/
               piix_Level = SIS630;
               break;
            default:
               piix_Level = NOPIIX;
               break;                                            /*AAAAAAAA*/
        }                                                         /*@V182487*/

       /***********************************************/
       /* Save PCI Bus Master DMA controller register */
       /* addresses for use later                     */
       /***********************************************/
       npACB->BMICOM = npAT->BM_BaseAddress;
       npACB->BMISTA = npAT->BM_BaseAddress + 2;
       npACB->BMIDTP = npAT->BM_BaseAddress + 4;

       /*************************************/
       /*      Program the PIIX Chip        */
       /*************************************/

       if( ProgramPCIChip( npACB, npAT, i, piix_Level )) {          
          continue;
       }

       /******************************************/
       /* Configure each unit to proper transfer */
       /* mode for PIO and/or DMA                */
       /******************************************/

       for ( j=0; j < npAT->cUnits; j++ )                            /* [003] *//*@V163508*/
       {
         SelectUnit( npACB, j );             /* select unit */

         if ( (npAT->Unit[j].Status == UTS_OK) &&                              /* [003] */
             !( CheckReady( npACB ) && (npACB->TimerFlags & ACBT_BUSY) ) )   /* check ready status *//* [003][018.2] */
         {
            DISABLE                             /* [003] disable interrupts */

            if ( npAT->BM_Overides & (ATBM_NOBM_UNIT0 << j) )
            {
              npACB->UnitCB[j].Flags &= ~UCBF_BM_DMA;
            }

            #ifdef DMA_DEFAULT_OFF                                              /*@V162458*/
             if ( !(npAT->BM_Overides & (ATBM_BM_UNIT0 << j)) )        /*@V162458*/
             {                                                         /*@V162458*/
               npACB->UnitCB[j].Flags &= ~UCBF_BM_DMA;                 /*@V162458*/
             }                                                         /*@V162458*/
            #endif                                                               /*@V162458*/

            if( npACB->UnitCB[j].Flags & UCBF_ATAPIDEVICE )
            {
              if ( !(npAT->BM_Overides & (ATBM_BM_UNIT0 << j)) ) {
                 npACB->UnitCB[j].Flags &= ~UCBF_BM_DMA;
              }
            }

            /*****************************************/
            /* Set Device DMA mode (ULTRA or MW DMA) */
            /*****************************************/

            if( npACB->UnitCB[j].Flags & UCBF_BM_DMA )
            {
               SetDeviceDMAMode( j, npAT, npACB, piix_Level );
            }

            /*********************************/
            /*      Set Device PIO mode      */
            /*********************************/

            SetDevicePIOMode( j, npAT, npACB );

            npACB->Flags &= ~ACBF_INTERRUPT;
            DevHelp_EOI( npACB->IntLevel );                  /* [003] clear IRQ */
            ENABLE                                   /* [003] enable interrupts */
         }
       }

       SelectUnit( npACB, 0 );               /* reselect unit 0 */
     }
     else
     {
        /**************************/
        /* No Adapter BM DMA Flag */
        /* so disable unit BM DMA */
        /**************************/

        DisableAdapterBMDMA( npAT, npACB );
     }

     /*************************/
     /* Set DMA Capable Bits  */
     /*************************/

     SetDMAcapableBits( npACB );
   }
}

/*--------------------------------------------*/
/* CalculateAdapterTiming                     */
/* ----------------------                     */
/*                                            */
/* Arguments:                                 */
/*      npACB = nptr to Adapter Control Block */
/*                                            */
/* Actions:                                   */
/*                                            */
/*      Picks optimal settings for PIO/DMA    */
/*      transfer for the selected ACB.        */
/*                                            */
/* Returns:                                   */
/*      Nothing                               */
/*                                            */
/*                                            */
/*--------------------------------------------*/

VOID CalculateAdapterTiming( NPACB npACB, USHORT piix_Level, USHORT uCable ) /*[009.2]*/
{
  USHORT      BestDMAMode,BestPIOMode;
  USHORT      DMASetting,PIOSetting;
  USHORT      i,shift_value;
  USHORT      PCIClockIndex = 0;

  /* Matrix for optimum PIO timings */
  /*                                */
  /* The idea of this table is to   */
  /* represenp normalized timing    */
  /* modes. Rather than figuring on */
  /* separate tables for PIO/DMA    */
  /* we will normalize all timings  */
  /* to PIO modes (even future DMA  */
  /* modes without corresponding    */
  /* PIO modes can be normalized to */
  /* artificial PIO modes. This can */
  /* then be used to determine the  */
  /* basic timing information for   */
  /* programming into the chip.     */
  /*                                */
  /* Dx = Drive x, Mx = Mode x      */
  /*                                */
  /*      D1M0 D1M1 D1M2 D1M3 D1M4  */
  /* D0M0 0000 0000 0002 0003 0004  */
  /* D0M1 0000 0000 0002 0003 0004  */
  /* D0M2 0200 0200 0202 0003 0004  */
  /* D0M3 0300 0300 0300 0303 0303  */
  /* D0M4 0400 0400 0400 0303 0404  */

  static USHORT BestPIOTable[5][5] =
  {
    { 0x0000, 0x0000, 0x0002, 0x0003, 0x0004 },
    { 0x0000, 0x0000, 0x0002, 0x0003, 0x0004 },
    { 0x0200, 0x0200, 0x0202, 0x0003, 0x0004 },
    { 0x0300, 0x0300, 0x0300, 0x0303, 0x0303 },
    { 0x0400, 0x0400, 0x0400, 0x0303, 0x0404 }
  };

  /**********************************/
  /* Pick correct clock index value */
  /**********************************/

  /* always use 33Mhz timings */

//  if ( SystemFlags & SYSFLG_CLK25 )
//  {
//     PCIClockIndex = 1;                    /* 25 Mhz timings */
//  }

  /****************************************************************/
  /* If >= PIIX3 then determine best PIO/DMA timing for each unit */
  /* For the MASTER unit save the timing in PIIX_IDEtim,          */
  /* and for the SLAVE, save timing in piix3_SIDEtim.             */
  /****************************************************************/

  if( piix_Level >= PIIX3 )                        /* PIIX3 and beyond */
  {
     for( i = 0; i < npACB->cUnits; i++ )          /* For each attached unit */
     {
        /**************************************/
        /* Find optimum interface timing mode */
        /**************************************/

        DeviceTimingMode( i, npACB );

        /************************/
        /* Set PIOsetting value */
        /************************/

        PIOSetting = npACB->UnitCB[i].InterfaceTiming;

        /**********************************/
        /* shift value for piix3_SIDEtim: */
        /* primary - 8 , secondary - 4    */
        /**********************************/
        shift_value = uCable ? 8 : 4;

        /******************************************/
        /* Choose PIIX timing register base value */
        /******************************************/

        TimeRegBaseValue( i, shift_value, PIOSetting, PCIClockIndex, npACB );

        /*************************************************/
        /* Select timing for each Device unit on channel */
        /*************************************************/

        SelectDeviceTiming( i, npACB, piix_Level );

        /***********************************************/
        /* Force compatible mode on PIO if we can run  */
        /* fast DMA but not fast PIO                   */
        /***********************************************/

        ForceComptMode( i, npACB );
     }
  } else                                              /* PIIX less than PIIX3 */
  if( npACB->cUnits == 2 )
  {
     /*************************************/
     /* Choose best timing mode valid     */
     /* for device 0/1 whether PIO or DMA */
     /*************************************/

     DeviceTimingMode( 0, npACB );
     DeviceTimingMode( 1, npACB );

     /***********************************/
     /* Find Optimum timing for Channel */
     /***********************************/

     BestPIOMode = BestPIOTable[npACB->UnitCB[0].InterfaceTiming]
                               [npACB->UnitCB[1].InterfaceTiming];

     npACB->UnitCB[1].InterfaceTiming = BestPIOMode & 0x00FF;
     npACB->UnitCB[0].InterfaceTiming = BestPIOMode >> 8;

     /*******************************************/
     /* Pick highest speed PIO mode for channel */
     /*******************************************/

     if ( npACB->UnitCB[0].InterfaceTiming > npACB->UnitCB[1].InterfaceTiming )
     {
        PIOSetting = npACB->UnitCB[0].InterfaceTiming;
     } else {
        PIOSetting = npACB->UnitCB[1].InterfaceTiming;
     }

     /******************************************/
     /* Choose PIIX timing register base valuep*/
     /******************************************/

     TimeRegBaseValue( npACB->cUnits, 0, PIOSetting, PCIClockIndex, npACB );

     /******************************************/
     /* Select timing for each unit on channel */
     /******************************************/

     SelectDeviceTiming( 0, npACB, piix_Level );
     SelectDeviceTiming( 1, npACB, piix_Level );

     /***********************************************/
     /* Force compatible mode on PIO if we can run  */
     /* fast DMA but not fast PIO                   */
     /***********************************************/

     ForceComptMode( 0, npACB );
     ForceComptMode( 1, npACB );
  }
  else                                /* only 1 attached device (Master only) */
  {
     /**************************************/
     /* Find optimum interface timing mode */
     /**************************************/

     DeviceTimingMode( 0, npACB );

     /******************************************/
     /* Find highest PIO speed for the channel */
     /******************************************/

     PIOSetting = npACB->UnitCB[0].InterfaceTiming;

     /******************************************/
     /* Choose PIIX timing register base value */
     /******************************************/

     TimeRegBaseValue( npACB->cUnits, 0, PIOSetting, PCIClockIndex, npACB );

     /******************************************/
     /* Select timing for ONLY unit on channel */
     /******************************************/

     SelectDeviceTiming( 0, npACB, piix_Level );

     /***********************************************/
     /* Force compatible mode on PIO if we can run  */
     /* fast DMA but not fast PIO                   */
     /***********************************************/

     ForceComptMode( 0, npACB );
  }
}

/*-------------------------------*/
/*                               */
/* DeviceTimingMode()            */
/*                               */
/*-------------------------------*/
VOID DeviceTimingMode( USHORT i, NPACB npACB )
{
   int j;

  /*************************************/
  /* Matrix to match DMA/PIO timings   */
  /*                                   */
  /* Rows are DMA modes,               */
  /* Cols are PIO modes                */
  /* result is PIO mode to program     */
  /*                                   */
  /*           P0 P1 P2 P3 P4          */
  /*        D0 00 00 00 03 04          */
  /*        D1 03 03 03 03 03          */
  /*        D2 04 04 04 03 04          */
  /*************************************/

  static UCHAR MatchPIODMATable[3][5] =
  {
     { 0x00, 0x00, 0x00, 0x03, 0x04 },
     { 0x03, 0x03, 0x03, 0x03, 0x03 },
     { 0x04, 0x04, 0x04, 0x03, 0x04 }
  };

   npACB->UnitCB[i].InterfaceTiming = npACB->UnitCB[i].CurPIOMode;

   if ( npACB->UnitCB[i].Flags & UCBF_BM_DMA )
   {
      j = MatchPIODMATable[npACB->UnitCB[i].CurDMAMode]
                          [npACB->UnitCB[i].InterfaceTiming];

      /**************************************/
      /* if j not equal to Current PIO mode */
      /* else interfacetiming = CurPIOMode  */
      /**************************************/

      if ( j != npACB->UnitCB[i].CurPIOMode )
      {
         if ( j < 3 )          /* if drive can't run at least MODE 3 timings */
         {                                   /* then turn off Bus Master DMA */
            npACB->UnitCB[i].Flags &= ~(UCBF_DMASGMODE | UCBF_BM_DMA);
         }
         else if ( ((j-1) == npACB->UnitCB[i].InterfaceTiming) &&
                   (npACB->UnitCB[i].InterfaceTiming >= 3) )
         {                           /* if only off 1 tick then adjust down */
            npACB->UnitCB[i].InterfaceTiming -= 1;
         }
         else
         {
            npACB->UnitCB[i].CurPIOMode = 0;  /* force PIO to mode 0 timings */
            npACB->UnitCB[i].InterfaceTiming = j;    /* use DMA mode timings */
         }
      }
   }
}

/*-------------------------------*/
/*                               */
/* TimeRegBaseValue()            */
/*                               */
/*-------------------------------*/
VOID TimeRegBaseValue( USHORT i, USHORT j, USHORT PIOSetting,
                                 USHORT PCIClockIndex, NPACB npACB )
{
   USHORT units, device;            /* Note =>i has a dual purpose */

   if (!j)                          /* no shift value implies less than PIIX3 */
   {
      units = i;                    /* Note=>i represents number of deivces */

      if( (PIOSetting == 3) || (PIOSetting == 4) )
      {
        npACB->PIIX_IDEtim = PIIX_ModeSets[PCIClockIndex][(PIOSetting-1)];

        if ( npACB->UnitCB[0].Flags & UCBF_BM_DMA )             /* Master - 0 */
        {
          npACB->UnitCB[0].CurDMAMode = (PIOSetting-2);
        }
        if( units == 2 )                                        /* Slave  - 1 */
        {
           if ( npACB->UnitCB[1].Flags & UCBF_BM_DMA )
           {
             npACB->UnitCB[1].CurDMAMode = (PIOSetting-2);
           }
        }
      } else {                                              /* PIOSetting < 3 */
         npACB->PIIX_IDEtim = PIIX_ModeSets[PCIClockIndex][0];
      }
   } else {
      /******************************/
      /* j implies PIIX3 and beyond */
      /* seperate device timing     */
      /* j represents shift value   */
      /******************************/
      device = i;                        /* Note=> i represents deivce number 0 or 1 */

      if( (PIOSetting == 3) || (PIOSetting == 4) )
      {
        if( device )                                          /* Slave timing */
        {
           npACB->PIIX_SIDEtim |= ( ( PIIX_ModeSets[PCIClockIndex][(PIOSetting-1)]
                           & 0x3000 ) >> ( j+2 ) ) |
           (( PIIX_ModeSets[PCIClockIndex][(PIOSetting-1)] & 0x0300 ) >>  j );

        } else {
           npACB->PIIX_IDEtim = PIIX_ModeSets[PCIClockIndex][(PIOSetting-1)];
        }

        if ( npACB->UnitCB[i].Flags & UCBF_BM_DMA )
        {
          npACB->UnitCB[i].CurDMAMode = (PIOSetting-2);
        }
      } else {                                              /* PIOSetting < 3 */
         if( device )
         {                                                    /* Slave timing */
            npACB->PIIX_SIDEtim |= ( ( PIIX_ModeSets[PCIClockIndex][0] & 0x3000 )
                          >> ( j+2 ) ) |
                      (( PIIX_ModeSets[PCIClockIndex][0] & 0x0300 ) >>  j );
         } else {
            npACB->PIIX_IDEtim = PIIX_ModeSets[PCIClockIndex][0];
         }
      }
   }
}

/*-------------------------------*/
/*                               */
/* SelectDeviceTiming()          */
/*                               */
/*-------------------------------*/
VOID SelectDeviceTiming( USHORT i, NPACB npACB, USHORT piix_Level )
{
   /*****************************************/
   /* if FAST timing on unit then enable it */
   /*****************************************/
   if( npACB->UnitCB[i].InterfaceTiming > 2 )
   {
     if( !i ) {                                                 /* Master - 0 */
        npACB->PIIX_IDEtim |= (ACBX_IDETIM_TIME0 | ACBX_IDETIM_IE0);
     } else {                                                    /* Slave - 1 */
        npACB->PIIX_IDEtim |= (ACBX_IDETIM_TIME1 | ACBX_IDETIM_IE1);

        if( piix_Level >= PIIX3 ) {           /* Slave Timing Register enable */
           npACB->PIIX_IDEtim |=  ACBX_IDETIM_ITE1;
        }
     }

     /*********************************************/
     /*  Enable Pre-Fetch/Posting if FIXED Disk   */
     /*********************************************/
     if( !(npACB->UnitCB[i].Flags & UCBF_ATAPIDEVICE) )
     {
        if( !i ) {                                       /* Master - 0 */
           npACB->PIIX_IDEtim |= ACBX_IDETIM_PPE0;
        } else {                                         /* Slave  - 1 */
           npACB->PIIX_IDEtim |= ACBX_IDETIM_PPE1;
        }
     }
   }
   else                                       /* slow timings, Disable BM DMA */
   {
     npACB->UnitCB[i].Flags &= ~(UCBF_DMASGMODE | UCBF_BM_DMA);
   }
}

/*-------------------------------*/
/*                               */
/* ForceComptMode()               */
/*                               */
/*---------------------h---------*/
VOID ForceComptMode( USHORT i, NPACB npACB )
{
   /********************************************************************/
   /* If device PIO capability is much slower than it's DMA capability */
   /********************************************************************/

   if ( (npACB->UnitCB[i].Flags & UCBF_BM_DMA) &&
        (npACB->UnitCB[i].InterfaceTiming) &&     /* if fast interface timing */
        !(npACB->UnitCB[i].CurPIOMode) )          /* but slow PIO */
   {
      /******************************/
      /* DMA Timing Enable Only bit */
      /******************************/
      if( !i ) {
         npACB->PIIX_IDEtim |= ACBX_IDETIM_DTE0;       /* Master - 0 */
      } else {
         npACB->PIIX_IDEtim |= ACBX_IDETIM_DTE1;       /* Slave  - 1 */
      }
   }
   else                                          /* report actual PIO setting */
   {
      npACB->UnitCB[i].CurPIOMode = npACB->UnitCB[i].InterfaceTiming;
   }
}

/*-------------------------------*/
/*                               */
/* SetDeviceDMAMode()            */
/*                               */
/* For ATA or ATAPI devices      */
/*                               */
/*-------------------------------*/
VOID SetDeviceDMAMode( USHORT j, NPATBL npAT, NPACB npACB, USHORT piix_Level )
{
   UCHAR UltraDMAMode;                                              

   /************************/
   /* Multi-Word DMA Setup */
   /************************/

   USHORT Port, Data;
   Port = npAT->BasePort + FI_PWRP;                      /* Features Register */
   Data = FX_SETXFERMODE;

   outp ( Port, Data );                                  /* Set TRANSFER Mode */
   IODelay();

   Port = npAT->BasePort + FI_PSECCNT;
   Data = npACB->UnitCB[j].CurDMAMode;
   Data |= FX_MWDMAMODEX;                               /* MultiWord DMA Mode */

   /*****************************************/
   /*   Check for and set Ultra DMA Mode    */
   /*****************************************/
   if( piix_Level > PIIX3 )                                         
   {
     if( npACB->UnitCB[j].UltraDMAMode )                      /* Ultra Device */
     {
        UltraDMAMode  = npACB->UnitCB[j].UltraDMAMode;

        UltraDMAMode &= ULTRADMAMASK;             /* Clear any unneeded bits */

        UltraDMAMode <<= 2;                                  /* Shift Left 2 */

        npACB->UnitCB[j].CurDMAMode = UltraDMAMode;   /* Save it for verbose */

        Data = GetUltraDMAMode( UltraDMAMode );  /* Set the Ultra DMA Mode n */

        Data |= FX_ULTRADMAMODEX;                      /* set ULTRA DMA mode */
     }
   }

   outp ( Port, Data );                                       /* Set DMA Mode */
   IODelay();

   npACB->Flags |= ACBF_INTERRUPT;

   Port = npAT->BasePort + FI_PCMD;                       /* Command Register */
   Data = FX_SETFEAT;

   outp ( Port, Data );                                       /* Set FEATURES */
   IODelay();

   CheckReady( npACB );                      /* do rdychk to clear int & wait */
}

/*-------------------------------*/
/*                               */
/* SetDevicePIOMode()            */
/*                               */
/* For ATA or ATAPI devices      */
/*                               */
/*-------------------------------*/
VOID SetDevicePIOMode( USHORT j, NPATBL npAT, NPACB npACB )
{
   USHORT Port, Data;

   Port = npAT->BasePort + FI_PWRP;                      /* Features Register */
   Data = FX_SETXFERMODE;

   outp ( Port, Data );                                  /* Set TRANSFER mode */
   IODelay();

   Port = npAT->BasePort + FI_PSECCNT;
   Data = npACB->UnitCB[j].CurPIOMode;
   if ( Data )                                           /* If not mode 0 PIO */
   {
     Data |= FX_PIOMODEX;                        /* set flow control PIO mode */
   } else {
     Data = FX_PIOMODE0;                                    /* set PIO mode 0 */
   }

   outp ( Port, Data );                                       /* Set PIO Mode */
   IODelay();

   npACB->Flags |= ACBF_INTERRUPT;

   Port = npAT->BasePort + FI_PCMD;                       /* Command Register */
   Data = FX_SETFEAT;

   outp ( Port, Data );                                       /* Set FEATURES */
   IODelay();

   CheckReady( npACB );               /* wait for ready although we ignore it */
}

/*-------------------------------*/
/*                               */
/* SetDMAcapableBits()           */
/*                               */
/* For ATA or ATAPI devices      */
/*                               */
/*-------------------------------*/
VOID SetDMAcapableBits(NPACB npACB)
{
    USHORT Data, Port;

    Data = 0;                                        /* Zero to no BM devices */

    /***************************************************************/
    /* We assume if ULTRA or MW DMA has been successfully set for  */
    /* the device then we still have the unit BM DMA flag          */
    /***************************************************************/

    if ( npACB->UnitCB[0].Flags & UCBF_BM_DMA )
    {
      Data |= ACBX_BMISTA_D0DMA;                   /* Device 0 DMA capable */
    }
    if ( npACB->UnitCB[1].Flags & UCBF_BM_DMA )
    {
      Data |= ACBX_BMISTA_D1DMA;                   /* Device 1 DMA capable */
    }

    Port = npACB->BMISTA;                          /* address BM Status reg */
    outp( Port, Data );                            /* write bits out */
}

/*-------------------------------*/
/*                               */
/* ProgramPIIXChip()             */
/*                               */
/*-------------------------------*/
USHORT ProgramPIIXPCI( NPACB npACB, NPATBL npAT, USHORT i, USHORT piix_Level )
{
   USHORT j, Data;
   ULONG  PCIData, TimingMask;

   npACB->PIIX_IDEtim  = 0;              /* clear timing value initially */
   npACB->PIIX_SIDEtim = 0;        /* clear Slave timing value initially */

   CalculateAdapterTiming( npACB, piix_Level, npAT->BasePort == FX_PRIMARY );

   /***************************************/
   /* Setup PIIX timing for this channel  */
   /***************************************/

   /*************************************/
   /* Write IDE timing Register         */
   /*************************************/

   if( npACB->PIIX_IDEtim )
   {
      if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                               (UCHAR) ( PCI_CR_BM_IDETIM + ((i == 0)
                                                                   ? 0 : 2) ),
                               (ULONG) npACB->PIIX_IDEtim,
                               2) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }
   }

   /**************************************/
   /* Get Master Latency Timer Value     */
   /**************************************/

   if( ReadPCIConfigSpace( &AdapterTable[i].PCIInfo,
                           PCI_CR_BM_MLT,
                           &PCIData,
                           1                        ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   if( (piix_Level < I82801AA) && (PCIData < 0x10) )
   {
      PCIData = 0x20;

      /*************************************/
      /* Write Master Latency Timer Value  */
      /*************************************/

      if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                               PCI_CR_BM_MLT,
                               PCIData,
                               1                        ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }
   }

   /*************************************/
   /* Read Bus Master Command Register  */
   /*************************************/

   if( ReadPCIConfigSpace( &AdapterTable[i].PCIInfo,
                           PCIREG_COMMAND,
                           &PCIData,
                           2                        ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   /*************************************/
   /* Enable Bus Master IDE Function    */
   /*************************************/

   if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                            PCIREG_COMMAND,
                            PCIData | PCI_CMD_BME,
                            2                       ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   /******************************************************/
   /* Allocate a page of physical memory and map it to   */
   /* this ACB's Bus Master Scatter/Gather list pointer. */
   /* Disable Bus Master DMA if this allocation fails.   */
   /******************************************************/

   if( AllocateBMSGL( npACB ) )                                  /*@V159438*/
   {                                                             /*@V159438*/
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }                                                             /*@V159438*/

   /*************************************/
   /* Setup PIIX SLAVE Timing Register. */
   /*************************************/

   /**************************************************/
   /* NOTE: PIIX3 and above support seperate timings */
   /**************************************************/

   if( npACB->PIIX_SIDEtim )
   {
      if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                               PCI_CR_BM_SIDETIM,
                               (ULONG) npACB->PIIX_SIDEtim,
                               1                        ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }
   }

   /*************************************************/
   /*               Ultra DMA Setup                 */
   /*************************************************/

   /*************************************/
   /* PIIX4 and above support ULTRA DMA */
   /*************************************/

   if( piix_Level > PIIX3 )
   {
      /**********************************/
      /* Get ULTRA DMA Control Register */
      /**********************************/

      if( ReadPCIConfigSpace( &AdapterTable[i].PCIInfo,
                              PCI_CR_BM_SDMACTL,
                              &PCIData,
                              1                        ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }

      /*-----------------------------------------*/
      /* Determine what drives support Ultra DMA */                 
      /*-----------------------------------------*/
      PCIData = DriveBitsSet( i, npACB, PCIData, PCI_CR_BM_SDMACTL );

      /************************************/
      /* Write ULTRA DMA Control Register */
      /************************************/
      if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                               PCI_CR_BM_SDMACTL,
                               PCIData,
                               1                        ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }

      /*--------------------------------------*/
      /* ATA/66 Ultra DMA additional register */
      /*--------------------------------------*/
      if( piix_Level == PIIX4 )                                      
      {
         AdjustDrive4PIIX4( npACB );                        /* Limit 2 UDMA33 */
      } else {
         /*--------------------------------*/
         /* Get ULTRA DMA Control Register */
         /*--------------------------------*/
         if( ReadPCIConfigSpace( &AdapterTable[i].PCIInfo,
                                 PCI_CR_BM_IDECONFIG,
                                 &PCIData,
                                 2                        ) )
         {
            DisableAdapterBMDMA( npAT, npACB );
            return( 1 );
         }

         AdjustUDMA( PCIData, i, npACB, piix_Level );               

         /*-----------------------------------------------------------*/
         /* What drives are UDMA Modes 3, 4 or 5   33 vs 66 vs 100Mhz */
         /*-----------------------------------------------------------*/
         PCIData = DriveBitsSet( i, npACB, PCIData, PCI_CR_BM_IDECONFIG );

         PCIData |= WR_PING_PONG_ENABLE;    /* Improved PIO Performance */

         /*----------------------------------*/
         /* Write the IDE IO Config Register */
         /*----------------------------------*/
         if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                                  PCI_CR_BM_IDECONFIG,
                                  PCIData,
                                  2                        ) )
         {
            DisableAdapterBMDMA( npAT, npACB );
            return( 1 );
         }
      }
      /*********************************/
      /* Get ULTRA DMA Timing Register */
      /*********************************/
      if( ReadPCIConfigSpace( &AdapterTable[i].PCIInfo,
                              PCI_CR_BM_SDMATIM,
                              &PCIData,
                              2                        ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }

      /*-----------------------------------*/              
      /* Determine Ultra DMA drive timings */
      /*-----------------------------------*/
      for( j=0; j < npACB->cUnits; j++ )
      {
         if( npACB->UnitCB[j].UltraDMAMode )              /* Ultra Device */
         {
            Data = npACB->UnitCB[j].UltraDMAMode;         /* Modes  43210 */
            if( Data > 7 )
            {
               Data >>= 3;                                /* ATA/66 43xxx */
               if( Data > 3 )
               {
                 Data >>= 2;                              
               }
            }else{
               Data >>= 1;                                /* ATA/33 xx210 */
            }
            TimingMask = 0x0003;
            if( j ) {
               Data       <<= 4;                         /* Shift for Drive 1 */
               TimingMask <<= 4;
            }
            if( i ) {
               Data       <<= 8;            /* Shift for Secondary Controller */  
               TimingMask <<= 8;                                    
            }
            PCIData &= ~TimingMask;         /* Clear timing bits for drive */
            PCIData |= Data;                /* Set new timing bits         */
         }
      }

      /***********************************/
      /* Write ULTRA DMA Timing Register */
      /***********************************/
      if( WritePCIConfigSpace( &AdapterTable[i].PCIInfo,
                               PCI_CR_BM_SDMATIM,
                               PCIData,
                               2                       ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }
   }
   /**********************************/
   /* End of Ultra DMA Settings      */
   /**********************************/

   return( 0 );                                           /* Success */
}

/*-------------------------------*/
/*                               */
/* DisableAdapterBMDMA()         */
/*                               */
/*-------------------------------*/
VOID DisableAdapterBMDMA( NPATBL npAT, NPACB npACB )
{
   npAT->Flags &= ~ATBF_BM_DMA;                   /* Adapter DMA Flag */

   npACB->UnitCB[0].Flags &= ~UCBF_BM_DMA;           /* Unit 0 DMA */

   if( npACB->cUnits == 2 )
   {
      npACB->UnitCB[1].Flags &= ~UCBF_BM_DMA;        /* Unit 1 DMA */
   }
}

/*-------------------------------*/                                /*@V159438 */
/*                               */                                /*@VVVVVVV */
/* AllocateBMSG()                */
/*                               */
/*-------------------------------*/

/***********************************************************************/
/* The Bus Master Scatter/Gather table is allpcated in physicalp       */
/* memory and does not move, it is reused for each BM DMA operation.   */
/*                                                                     */
/* 1. Allocate a GDT selector.                                         */
/* 2. Allocate physical memory for scatter/gather table.               */
/* 3. Map the physical memory to the GDT selector.                     */
/*                                                                     */
/* Disable Bus Master DMA if any part of this allocation fails.        */
/***********************************************************************/
BOOL NEAR AllocateBMSGL( NPACB npACB )
{
   ULONG           PhysAddr;

   /*
   ** Initialize the outputs that live in the ACB.
   */
   npACB->pBMDMA_SGL        = NULL;
   npACB->BMDMA_SGListSize  = 0;
   npACB->BMDMA_SGListCount = 0;

   if( DevHelp_AllocGDTSelector( &npACB->BMDMA_GDT, 1 ) )
   {
      /* Error - disable DMA on this adapter */
      return( TRUE );
   }

   if( DevHelp_AllocPhys( MR_4K_LIMIT, 0, &PhysAddr ) )
   {
      /* Error - disable DMA on this adapter */
      DevHelp_FreeGDTSelector( npACB->BMDMA_GDT );
      return( TRUE );
   }

   /*
   ** Calculate useable size of scatter/gather table. Restrictions are:
   ** 1) Cannot span a 4KB boundary
   ** 2) must be ULONG aligned
   **
   ** store resulping physical address, size in bytes of useable area,
   ** and number of descriptors permitted.
   */

   if( (npACB->BMDMA_SGListSize = (PhysAddr & (MR_4K_LIMIT-1))) <=
                                  (MR_4K_LIMIT/2) )
   {
      /* Larger half is before 4KB boundary */

      npACB->BMDMA_SGList = (PhysAddr & ~3L); /* ULONG align it */
      if ( PhysAddr & 3 )
      {
         npACB->BMDMA_SGList += 4;         /* keep in boundaries */
      }
      npACB->BMDMA_SGListSize = MR_4K_LIMIT - npACB->BMDMA_SGListSize;
      npACB->BMDMA_SGListSize &= ~3L;     /* adjust size for ULONGs */
      if ( PhysAddr & 3 )
      {
         npACB->BMDMA_SGListSize -= 4;     /* keep in boundaries */
      }
   }
   else
   {
      /*dBegin [012] - larger half is after 4KB boundary */

      npACB->BMDMA_SGList = (PhysAddr - npACB->BMDMA_SGListSize) + MR_4K_LIMIT;
      npACB->BMDMA_SGListSize &= ~3L;     /* adjust size for ULONGs */
      /* End [012] */
   }

   if( DevHelp_PhysToGDTSelector( npACB->BMDMA_SGList,
                                  npACB->BMDMA_SGListSize,
                                  npACB->BMDMA_GDT ) )
   {
      /* Error - disable DMA on this adapter */
      DevHelp_FreePhys( PhysAddr );
      DevHelp_FreeGDTSelector( npACB->BMDMA_GDT );
      return( TRUE );
   }

   /*
   ** It worked OK, so save pointer to Scatter/Gather list in ACB.
   */
   SELECTOROF(npACB->pBMDMA_SGL) = npACB->BMDMA_GDT;
   OFFSETOF(npACB->pBMDMA_SGL) = 0;
   npACB->BMDMA_SGListCount = npACB->BMDMA_SGListSize / 8;

   return( FALSE );                                     /*AAAAAAAA*//*AAAAAAAA*/
}                                                       /*@V159438*//*@V179942*/
                                                                    /*@V193287*/
/*----------------------------------*/
/*                                  */
/* DeterminePCIChips()              */
/*                                  */
/*----------------------------------*/
USHORT  DeterminePCIChips( USHORT SystemType )
{
  USHORT rc = FALSE;

  /*-------------------------------------------*/                   /*@V119250*/
  /* Check for the first CMD640x PCI chip.     */                   /*@V119250*/
  /*-------------------------------------------*/                   /*@V119250*/
  if( !(rc = PresenseCheckPCI( &AdapterTable[0],                    /*@V129765*/
                               PCIDevice[PCID_CMD640_0] )) )        /*@V129765*/
  {                                                                 /*@V119250*/
     /* Something special for CMD640 */                             /*@V147576*/
  }
  /*-------------------------------------------*/                   /*@V129765*/
  /* Check for the first RZ1000 PCI chip.      */                   /*@V129765*/
  /*-------------------------------------------*/                   /*@V129765*/
  else if( !(rc = PresenseCheckPCI( &AdapterTable[0],                    /*@V129765*/
                               PCIDevice[PCID_RZ1000_0] )) )        /*@V129765*/
  {                                                                 /*@V129765*/
     /* Something special for RZ1000 */                             /*@V129765*/
  } else {

     Search4PCIChips();                                             

     SystemType = AdjustIfPIIXFound( SystemType );                  /*@V193287*/
  }
  return( SystemType );
}

/*----------------------------------*/
/*                                  */
/*  Search4PCIChips()               */
/*                                  */
/*----------------------------------*/
VOID  Search4PCIChips( VOID )
{
   UCHAR    i   = 0;
   UCHAR    Row = 0;
   USHORT   SBDeviceID;
   UCHAR    AnyPCIFound = 0;
   ULONG    PCIData;

   /*--------------------------------------------------------------*/
   /* PIIXList is an array of PIIX chips that will be searched for */
   /* at Index 0 and Index 1.  If detected we will fill in the     */
   /* Array PIIXes with the PIIX information for that chip.        */
   /* Note: AdapterTable is just used for placeholder for info at  */
   /* first and then is filled in later in CkandValidateChannels   */
   /*--------------------------------------------------------------*/

   static USHORT PIIXList[14][3] =                                   /*@VXXXXXX*/
   {
      { PCID_INTEL82371FB,      PIIX_PCIIDE_DEV_ID , 0 }, /* PIIX  */
      { PCID_PIIX3       ,     PIIX3_PCIIDE_DEV_ID , 0 }, /* PIIX3 */
      { PCID_PIIX4       ,     PIIX4_PCIIDE_DEV_ID , 0 }, /* PIIX4 */
      { PCID_82801AA     ,  I82801AA_PCIIDE_DEV_ID , 0 }, /* 82801 */ 
      { PCID_82801AB     ,  I82801AB_PCIIDE_DEV_ID , 0 }, /* 82801 */ 
      { PCID_82801BA_1   , I82801BA1_PCIIDE_DEV_ID , 1 }, /* 82801 */ 
      { PCID_82801BA_2   , I82801BA2_PCIIDE_DEV_ID , 1 }, /* 82801 */ 
      { PCID_82801BA_3   , I82801BA3_PCIIDE_DEV_ID , 1 }, /* 82801 */ 
      { PCID_82801BA_4   , I82801BA4_PCIIDE_DEV_ID , 0 }, /* 82801 */ 
      { PCID_82801BA_5   , I82801BA5_PCIIDE_DEV_ID , 0 }, /* 82801 */ 
      { PCID_CMD646      ,    CMD646_PCIIDE_DEV_ID , 0 }, /* CMD646*/ 
      { PCID_CMD648      ,    CMD648_PCIIDE_DEV_ID , 0 }, /* CMD648*/ /*@VXXXXXX*/
      { PCID_VIA596      ,    VIA596_PCIIDE_DEV_ID , 0 }, /* VIA 0571 V@253487*/
      { PCID_SIS630      ,    SIS630_PCIIDE_DEV_ID , 0 }  /* SiS 5513 V@253487*/

   };

   for ( i=0; i < MAXPIIXSEARCH; i++ )
   {                                                      /* ROW Calculation */
      if( i == 0 ) {                                          
         Row = 0;
      } else if (!(i & 0x01)){
         Row += 1;
      }

      PCIDevice[ PIIXList[Row][0] ].Ident.Index = (i & 0x01); /* 0 or 1 ONLY */

      if( !( PresenseCheckPCI( &AdapterTable[0],
                               PCIDevice[ PIIXList[Row][0] ] )))
      {
         PIIXes[i].PCIInfo.Ident       = PCIDevice[PIIXList[Row][0]].Ident;
         PIIXes[i].PCIInfo.Ident.Revision = AdapterTable[0].PCIInfo.Ident.Revision;  
         PIIXes[i].npPCIDeviceMsg      = PCIDevice[PIIXList[Row][0]].npDeviceMsg;
         PIIXes[i].PCIInfo.Ident.Device= PIIXList[Row][1];
         PIIXes[i].PCIInfo.FirstConfigRec = AdapterTable[0].PCIInfo.FirstConfigRec;
         PIIXes[i].PCIInfo.BusNum      = AdapterTable[0].PCIInfo.BusNum;
         PIIXes[i].PCIType             = PIIXList[Row][0];        
         PIIXes[i].PCIInfo.DevFunc     = AdapterTable[0].PCIInfo.DevFunc;
         PIIXes[i].PCIInfo.DevFunc    &= 0xF8;   /* mask off function number */


         if( (PIIXes[i].PCIType < PCID_CMD646) || (PIIXes[i].PCIType > PCID_CMD648_1) )
         {
            PIIXes[i].PCIInfo.DevFunc    |= 0x01;   /* PIIX IDE is function 1 */
         }

         //start V@253487

         /******************************************************************/
         /* VIA IDE controller has same DevID for 568, 596 and 686 chipsets*/
         /* Have to check south bridge revision for identification.        */
         /******************************************************************/

         if( (PIIXes[i].PCIType > PCID_CMD648_1) && (PIIXes[i].PCIType < PCID_SIS630) ) 
         {

            AdapterTable[0].PCIInfo.DevFunc &= 0xF8;                 // Change to function 0

            if( !(ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                      PCI_CONFIG_REG_DID,
                                      (PULONG)&SBDeviceID,
                                      2                        ) ))
            {
               switch(SBDeviceID)
               {
               case 0x596 : break;
               case 0x686 : PIIXes[i].npPCIDeviceMsg = VIA686Msgtxt;
                  break;
               default : PIIXes[i].npPCIDeviceMsg = VIA586Msgtxt;
                  break;
               }
            }
         }

         //end V@253487

         if( PIIXList[Row][2] )
         {
            AdapterTable[0].PCIInfo = PIIXes[i].PCIInfo;

            if( ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                    PCI_CONFIG_REG_DID,
                                    &PCIData,
                                    2                        ) )
            {
               PIIXes[i].PCIInfo.Present = FALSE;     /* Different Funct 1 ID */
            }
            else if( PCIData == PIIXList[Row][1] )
            {
               PIIXes[i].PCIInfo.Present = TRUE;        /* Funct 1 ID Matches */
               AnyPCIFound = 1;
            } else {
               PIIXes[i].PCIInfo.Present = FALSE;     /* Different Funct 1 ID */
            }
         } else {
            PIIXes[i].PCIInfo.Present = TRUE;         /* Only 1 Funct 1 ID */
            AnyPCIFound = 1;
         }
      }
   }

   if( AnyPCIFound )                                     /* Any PCI Present */
   {
      CkandValidateChannels();
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/
VOID CkandValidateChannels()
{
   UCHAR     i;
   NPATBL    npAT;

   for( i=0; i < MAXPIIXSEARCH; i++)
   {
      if( PIIXes[i].PCIInfo.Present == TRUE )
      {
          AdapterTable[0].PCIInfo = PIIXes[i].PCIInfo;
          Channels( i, PIIXes[i].PCIType );                          
      }
   }

   /*----------------------------------*/
   /* Validate Controllers             */
   /*----------------------------------*/
   npAT = AdapterTable;                                                
   for( i=0; i < MAX_ADAPTERS; i++, npAT++ )
   {
      TurnOffPCI( npAT );                   /* Clean any spurious data */
   }

   npAT = AdapterTable;

   if(  CopyChannelPCIinfo( npAT, FX_PRIMARY ))
   {
      BusMasterBaseAddress( npAT, FX_PRIMARY );
      npAT++;
   }

   if(  CopyChannelPCIinfo( npAT, FX_SECONDARY ))
   {
      BusMasterBaseAddress( npAT, FX_SECONDARY );
      npAT++;
   }

   if(  CopyChannelPCIinfo( npAT, NATIVE_PRIMARY_CHANNEL ))
   {
      BusMasterBaseAddress( npAT, NATIVE_PRIMARY_CHANNEL );
      npAT++;
   }

   if(  CopyChannelPCIinfo( npAT, NATIVE_SECONDARY_CHANNEL ))
   {
      BusMasterBaseAddress( npAT, NATIVE_SECONDARY_CHANNEL );
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/
VOID Channels( UCHAR i, UCHAR PCIType )
{
   UCHAR     j;
   ULONG     Reg_Data;
   BOOL      IDEMode;

   IDEMode = NativeOrLegacyMode( &AdapterTable[0], PCIType );

   /*----------------------------------------*/
   /* Ck 4 Primary and/or Secondary Channels */
   /*----------------------------------------*/
   if( PCIType < PCID_CMD646 )                                        /* PIIX */
   {
      for ( j = 0; j < 2; j++ )
      {
         if( !(ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                   (UCHAR) (PCI_CR_BM_IDETIM+(2*j)),
                                   &Reg_Data,
                                   (USHORT) 2           ) ))
         {
            /*--------------------------*/
            /* Check Channel Enable Bit */
            /*--------------------------*/
            if( Reg_Data & IDETIM_IDE )
            {
               PIIXes[i].Channels |= ((1+j) << IDEMode);
            }
         }
      }
   }else if ( (PCIType > PCID_82801BA_5_1) && (PCIType < PCID_VIA596 ) ){    

      /*-----------------------------------*/
      /* CMD Primary and Secondary Inquiry */                            /*CMD*/
      /*-----------------------------------*/
      if( !(ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                CNTRL,
                                &Reg_Data,
                                (USHORT) 1           ) ))
      {
         /*--------------------------*/
         /* Check Channel Enable Bit */
         /*--------------------------*/
         if( Reg_Data & 0x04 )
         {
            PIIXes[i].Channels |= ( PRIMARY_BIT << (2*IDEMode) );
            PIIXes[i].PCIInfo.InterruptLine = GetPCIIntLine( &PIIXes[i].PCIInfo );
            SetIRQ_Shared_bits( PIIXes[i].PCIInfo.InterruptLine );
         }
         if( Reg_Data & 0x08 )
         {
            PIIXes[i].Channels |= ( SECONDARY_BIT << (2*IDEMode) );
            PIIXes[i].PCIInfo.InterruptLine = GetPCIIntLine( &PIIXes[i].PCIInfo );
            SetIRQ_Shared_bits( PIIXes[i].PCIInfo.InterruptLine );
         }
      }
   }
   else if ( (PCIType > PCID_CMD648_1 ) && (PCIType < PCID_SIS630) )  /* VIA */  
   {
      for ( j = 0; j < 2; j++ )
      {
         if( !(ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                   (UCHAR) (PCI_CR_BM_IDETIM),
                                   &Reg_Data,
                                   (USHORT) 1           ) ))
         {
            /*--------------------------*/
            /* Check Channel Enable Bit */
            /*--------------------------*/
            if( Reg_Data & (j+1))
            {
               PIIXes[i].Channels |= (1+j);
            }
         }
      }
   }

   else if (PCIType > PCID_VIA596_1)
   {
      for ( j = 0; j < 2; j++ )
      {
         if( !(ReadPCIConfigSpace( &AdapterTable[0].PCIInfo,
                                   (UCHAR) (PCI_CR_BM_SDMATIM),
                                   &Reg_Data,
                                   (USHORT) 1           ) ))
         {
            /*--------------------------*/
            /* Check Channel Enable Bit */
            /*--------------------------*/
            if(((Reg_Data >> 1) & (j+1)))
            {
               PIIXes[i].Channels |= (1+j);
            }
         }
      }
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/
USHORT TurnOffPCI( NPATBL npAT )
{
   npAT->PCIInfo.Present = FALSE;
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/
USHORT  AdjustIfPIIXFound( USHORT SystemType )
{
  USHORT    System_Type;
  ULONG     TritonRev = 0;
  USHORT    i;

  static    PCI_INFO  PciInfo;

  System_Type = SystemType;                       /* Get existing system type */

  for (i = 0; i < 2; i++ )                        /* Turn on if PCI present */
  {
     if( AdapterTable[i].PCIInfo.Present == TRUE )
     {
        /*------------------------*/
        /* Default for valid PIIX */
        /*------------------------*/
        System_Type |= SDMA_TYPE_F;             /* PCI so must have type F */
        System_Type |= SDMA_SG;                          /* scatter/gather */
        System_Type |= SDMA_BusMaster;                   /* Bus Master DMA */
     }
  }

  for (i = 0; i < 2; i++ )
  {
     if( AdapterTable[i].PCIInfo.Present == TRUE )
     {
        if( (AdapterTable[i].PCIInfo.Ident.Device == PIIX_PCIIDE_DEV_ID )
         && (AdapterTable[i].PCIInfo.Ident.Revision < 2) )
        {
           /*-----------------------------------*/
           /* Rev is not capable of proper DMA. */
           /*-----------------------------------*/
           System_Type &= ~( SDMA_BusMaster | SDMA_SG );
        }
        if( AdapterTable[i].PCIInfo.Ident.Device == PIIX_PCIIDE_DEV_ID )
        {
           /*-------------------------------------------*/
           /* Check for the ORION chip.                 */
           /*-------------------------------------------*/
           setmem( (PBYTE)&PciInfo, 0, sizeof(PciInfo) );

           if( !FindPCIDevice( &PciInfo, PCIDevice[PCID_INTEL_ORION] ) )
           {
              /*-----------------------------------------*/
              /* Found Orion chipset, check revision id. */
              /*-----------------------------------------*/
              if( ReadPCIConfigSpace( &PciInfo, 8, &TritonRev, 1 ) )
              {
                 System_Type &= ~( SDMA_BusMaster | SDMA_SG );
              }
              else if( TritonRev < 4 )
              {
                 System_Type &= ~( SDMA_BusMaster | SDMA_SG );
              }
           }
        }
     }
  }
  return( System_Type );                                            /*@AAAAAAA*/
}                                                                   /*@V193287*/

/*-------------------------------*/                                 
/*                               */                                 /*@VVVVVVV*/
/* DriveBitsSet()                */
/*                               */
/*-------------------------------*/
ULONG DriveBitsSet( USHORT i, NPACB npACB, ULONG PCIData, USHORT Register )
{
   USHORT j, Data;
   USHORT Ultramode;

   for( j=0; j < npACB->cUnits; j++ )
   {
       Data = ( j + 1 );
       if( i ) {                            /* shift for Secondary Controller */
         Data <<= 2;
       }
       Ultramode = npACB->UnitCB[j].UltraDMAMode;

       /*-----------------------------------------------*/
       /*         IDE IO Config Register                */
       /*-----------------------------------------------*/
       if( Register == PCI_CR_BM_IDECONFIG )
       {
           if( Ultramode > ULTRADMAMODE_2 )
           {
              if( Ultramode > ULTRADMAMODE_4 )
              {                                              /* ATA100       */
                 PCIData &= ~Data;                           /* Turn Off Bit */
                 PCIData |= ( Data <<= 12 );                 /* Turn On  Bit */
              } else {
                                                             /* ATA 66       */
                 PCIData |=  Data;                           /* On  */
                 PCIData &= ~( Data <<= 12 );                /* Off */
              }
           } else {                                          /* ATA 33       */
              PCIData &= ~Data;                              /* Off */
              PCIData &= ~( Data <<= 12 );                   /* Off */
           }
       } else {
          /*-----------------------------------------------*/
          /*         Ultra DMA Control Register            */
          /*-----------------------------------------------*/
          if( Ultramode )                                 /* Ultra Select Bit */
          {
             PCIData |=  Data;                                /* Turn On  Bit */
          } else {
             PCIData &= ~Data;                                /* Turn Off Bit */
          }
       }
   }
   return( PCIData );
}

/*-------------------------------*/
/* AdjustUDMA()                  */
/*                               */
/*                               */
/*-------------------------------*/
VOID AdjustUDMA( ULONG PCIData, USHORT i, NPACB npACB, USHORT piix_Level )
{
   USHORT j;
   ULONG  CableDetectBits = PCIData;

   CableDetectBits &= 0x00F0;
   CableDetectBits    >>= 4;          /* Primary   */
   if( i )
      CableDetectBits >>= 2;          /* Secondary */

   if( npACB->cUnits >= 2 )                                         
   {
      Adjustfor82801( npACB, piix_Level );
   }

   for( j=0; j < npACB->cUnits; j++ )
   {
      if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 )
      {
         /*-------------------------------------------------*/
         /* See if we have the Special IDE cable for ATA/66 */
         /*       1=80 Pin cable   0=40 Pin cable           */
         /*-------------------------------------------------*/
         if(!( CableDetectBits & (j+1) ))                          /* 1-80Pin */
         {                                                         /* 0-40Pin */
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_2;
         }

         /*-------------------------------------------------*/     
         /*         Limit to UDMA66 on I82801AA   ICH       */
         /*         Limit to UDMA66 on I82801BA_1 ICH       */
         /*-------------------------------------------------*/
         if( (piix_Level == I82801AA) || (piix_Level == I82801BA_1) )
         {
            if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_4 )
            {
               npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_4;
            }
         }

         /*-------------------------------------------------*/
         /*         Limit to UDMA33 on I82801AB ICH         */
         /*-------------------------------------------------*/
         else if( piix_Level == I82801AB )                          
         {
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_2;
         }
      }
   }                                                                /*@AAAAAAA*/
}                                                                   

/*-------------------------------*/
/* AdjustDrive4PIIX4             */
/*                               */
/*                               */
/*-------------------------------*/
VOID AdjustDrive4PIIX4( NPACB npACB )
{
   USHORT j;

   for( j=0; j < npACB->cUnits; j++ )
   {
      if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 )
      {
         npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_2;   /* Limit 2 UDMA 33 */
      }
   }                                                                /*@AAAAAAA*/
}                                                                   


/*-------------------------------*/
/* Adjustfor82801                */
/*                               */
/* We got here because we have 2 */
/* devices on a channel          */
/*                               */
/*  Unit 0  Unit 1    Adjust     */
/*  U66/100  <U66     Unit 0     */
/*  <U66    U66/100   Unit 1     */
/*-------------------------------*/
VOID Adjustfor82801( NPACB npACB, USHORT piix_Level )
{
   if(( piix_Level == I82801AA )   ||
      ( piix_Level == I82801BA_1 ) ||
      ( piix_Level == I82801BA_2 ) ||
      ( piix_Level == I82801BA_3 ))
   {
      if( npACB->UnitCB[0].UltraDMAMode > ULTRADMAMODE_2 )          /* UNIT 0 */
      {
         if( npACB->UnitCB[1].UltraDMAMode < ULTRADMAMODE_3 )       /* Unit 1 */
         {
            npACB->UnitCB[0].UltraDMAMode = ULTRADMAMODE_2;   /* Adjust Unit 0 */
         }
      } else {
         if( npACB->UnitCB[1].UltraDMAMode > ULTRADMAMODE_2 )       /* Unit 1 */
         {
            npACB->UnitCB[1].UltraDMAMode = ULTRADMAMODE_2;  /* Adjust Unit 1 */
         }
      }
   }                                                                /*@AAAAAAA*/
}                                                                   


/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/                                 
UCHAR GetPCIIntLine( NPPCI_INFO npPCIInfo )
{
   ULONG  Reg_Data;

   /*---------------------------------------------------------------*/
   /* This is the Interrupt Level that will be assinged to PCI INTA */
   /*---------------------------------------------------------------*/
   if( ReadPCIConfigSpace( npPCIInfo,
                           PCIREG_INT_LINE,       /* Interrupt Line Register */
                           &Reg_Data,
                           1              ))
   {
      Reg_Data = 0x0E;      /* Default to IRQ14 */
   }

   return( Reg_Data );
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/                                 
BOOL NativeOrLegacyMode( NPATBL npAT, UCHAR PCIType )
{
   BOOL   IDEMode;
   ULONG  BaseAddr;
   ULONG  One_Byte;

   if( PCIType < PCID_CMD646 )
   {
      IDEMode = LEGACY;
   } else if(( PCIType == PCID_CMD646 ) || ( PCIType == PCID_CMD648 )) {

      if( ReadPCIConfigSpace( &npAT->PCIInfo,
                              PCI_CONFIG_REG_PI,
                              &One_Byte,
                              1           ) )
      {
         IDEMode = NATIVE;   /* DEFAULT for CMD */
      }
      if( One_Byte == 0x8F || One_Byte == 0x8A ){
         IDEMode = NATIVE;
      } else if( One_Byte == 80 ){
         IDEMode = LEGACY;
      } else {
         IDEMode = NATIVE;   /* Default */
      }
   } else {                                 /* Future Expansion */
      IDEMode = LEGACY;
   }

   return( IDEMode );
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/                              
BOOL CopyChannelPCIInfo( NPATBL npAT, USHORT Channel )
{
   USHORT i, Channel_Bit;

   if( Channel == FX_PRIMARY )
   {
      Channel_Bit = PRIMARY_CHANNEL;
      Channel = 0;
   } else if( Channel == FX_SECONDARY ){
      Channel_Bit = SECONDARY_CHANNEL;
      Channel = 1;
   } else if( Channel == NATIVE_PRIMARY_CHANNEL ){
      Channel_Bit = NATIVE_PRIMARY_CHANNEL;
      Channel = 0;
   } else {
      Channel_Bit = NATIVE_SECONDARY_CHANNEL;
      Channel = 1;
   }

   for( i=0; i < MAXPIIXSEARCH; i++ )
   {
      if( PIIXes[i].PCIInfo.Present == TRUE )
      {
         if( PIIXes[i].Channels & Channel_Bit )
         {
            npAT->PCIInfo        = PIIXes[i].PCIInfo;
            npAT->npPCIDeviceMsg = PIIXes[i].npPCIDeviceMsg;
            npAT->Channel        = Channel_Bit;
            npAT->Flags         &=~ATBF_DISABLED;

            /*-------------------------------------------------*/
            /* Get new BasePort and BaseStatus register values */
            /*-------------------------------------------------*/
            if( Channel_Bit > SECONDARY_CHANNEL )
            {
               GetBaseAddresses( PIIXes[i].PCIType, Channel, npAT );

               if( PIIXes[i].PCIInfo.InterruptLine != 0 )
               {
                  npAT->IRQLevel = PIIXes[i].PCIInfo.InterruptLine;
               }
            }

            return( TRUE );                /* Found one */
            break;
         }
      }
   }
   return( FALSE );
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/                                 
VOID GetBaseAddresses( UCHAR PCIType, USHORT Channel, NPATBL npAT )
{
   ULONG  BaseAddr;

   if(( PCIType == PCID_CMD646 ) || ( PCIType == PCID_CMD648 ))
   {
      /*-----------------------*/
      /* Base Address Register */
      /*-----------------------*/
      if( ReadPCIConfigSpace( &npAT->PCIInfo,
                              ( PCIREG_IO_BASE +( 8 * Channel )),
                              &BaseAddr,
                              (USHORT) 4                        ) )
      {
         DisableAdapterBMDMA( npAT, npAT->npACB );      /* Turn off PCI Chip */
         return;
      }

      BaseAddr &= 0xFFF8;

      /*--------------------------------------------------------------------*/
      /* Only change the default legacy address if the register is not zero */
      /*--------------------------------------------------------------------*/
      if( BaseAddr )
      {
         npAT->BasePort = BaseAddr;
      }

      /*-------------------------*/
      /* Status Register Address */
      /*-------------------------*/
      if( ReadPCIConfigSpace( &npAT->PCIInfo,
                              ( PCIREG_MEMORY_BASE +( 8 * Channel )),
                              &BaseAddr,
                              4                 ) )
      {
         DisableAdapterBMDMA( npAT, npAT->npACB );      /* Turn off PCI Chip */
         return;
      }

      BaseAddr &= 0xFFFC;

      if( BaseAddr )
      {
         npAT->StatusPort = BaseAddr + 0x02;         /* Third byte is active */
         npAT->Flags     |= ATBF_CONTROLSPECIFIC;
      }
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */                              
/*----------------------------------*/
VOID  BusMasterBaseAddress( NPATBL npAT, USHORT channel )
{
   ULONG  PhysAddr, BM_AddressMask;
   USHORT i;

   /*--------------------------------------------*/
   /* Bus Master Interface Base Address Register */
   /*--------------------------------------------*/
   if( ReadPCIConfigSpace( &npAT->PCIInfo,
                           PCI_CR_BM_IBA,
                           &PhysAddr,
                           (USHORT) 4    ) )
   {
      /*-------------------------------------------*/
      /* Turn off PCI bus mastering on the channel */
      /*-------------------------------------------*/
      TurnOffPCI( npAT );

   } else {

      if( (channel == FX_PRIMARY) || (channel == NATIVE_PRIMARY_CHANNEL) )
      {
         i = 0;
      } else {
         i = 1;
      }

      /*---------------------------*/
      /* BM Interface Base Address */
      /*---------------------------*/
      if(( npAT->PCIInfo.Ident.Device == CMD646_PCIIDE_DEV_ID ) ||
         ( npAT->PCIInfo.Ident.Device == CMD648_PCIIDE_DEV_ID ) )    /*@VXXXXX*/
      {
         BM_AddressMask = 0xFFFFFFF0L;               /* CMD  BM Address */
      } else {
         BM_AddressMask = 0x0000FFF0L;               /* PIIX BM Address */
      }
      npAT->BM_BaseAddress=(((USHORT)(PhysAddr & BM_AddressMask))+(i*8));
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/                              
USHORT ProgramPCIChip( NPACB npACB, NPATBL npAT, USHORT i, USHORT piix_Level )
{
   if( piix_Level < CMD646 )
   {
      return( ProgramPIIXPCI(  npACB,
                               npAT,
                               i,
                               piix_Level ) );
   }
   else if ( (piix_Level >= CMD646) && (piix_Level < VIA586) )  
   {
      return(  ProgramCMDPCI(  npACB,
                               npAT,
                               piix_Level ) );
   }
   else if ( (piix_Level > CMD648) && (piix_Level < SIS630) )
   {
      return( ProgramVIAPCI(   npACB,                           
                               npAT,
                               i,
                               piix_Level ) );
   }
   else if (piix_Level > VIA686)                                
   {
      return( ProgramSISPCI(   npACB,
                               npAT,
                               piix_Level,
                               i          ) );
   }
   else
   {
      return( 1 );
   }
}

/*----------------------------------*/
/*                                  */
/*                                  */
/*----------------------------------*/                              
USHORT ProgramCMDPCI( NPACB npACB, NPATBL npAT, USHORT CMD_level )
{
   /*-------------------------------------------------*/
   /* Timing chart created for CMD 646 chip from spec */
   /* Assume Bus Speed       33 Mhz                   */
   /* Length of Clock Cycle  30Nsec                   */
   /*-------------------------------------------------*/
   static USHORT CMDRegs[13][4] =
   {  /* v---Drive Mode              /* Mode   v---- Cycle Time Values 1=30ns */
      /* |     v----ARTTIMx              |     | */
      /* |     |     v----CMDTIM         |     | */
      /* v     v     v     v----DRWTIMx  v     v */
    { 0x00, 0x80, 0xCF, 0x6E },      /* PIO0  20 */
    { 0x01, 0x40, 0xCF, 0x58 },      /* PIO1  13 */
    { 0x02, 0x40, 0xCF, 0x44 },      /* PIO2   8 */
    { 0x04, 0x40, 0x32, 0x32 },      /* PIO3   6 */
    { 0x08, 0x40, 0x3F, 0x3F },      /* PIO4   4 */

    { 0x01, 0x00, 0x00, 0x88 },      /* DMA0  13 */
    { 0x02, 0x00, 0x00, 0x32 },      /* DMA1   5 */
    { 0x04, 0x00, 0x00, 0x31 },      /* DMA2   3 */
                        /* v---------- Used to hold bit values for UDIDETCR0 */
    { 0x01, 0x00, 0x00, 0x30 },      /* UDMA0  4 */    /* 30 ns */
    { 0x02, 0x00, 0x00, 0x20 },      /* UDMA1  3 */
    { 0x04, 0x00, 0x00, 0x10 },      /* UDMA2  2 */
    { 0x08, 0x00, 0x00, 0x30 },      /* UDMA3  2 */    /* 15 ns */   /*@VXXXXX*/
    { 0x10, 0x00, 0x00, 0x20 }       /* UDMA4  1 */                  /*@VXXXXX*/
   };

#define PIO_LOWER    0
#define PIO_UPPER    4
#define DMA_LOWER    5
#define DMA_UPPER    7
#define UDMA_LOWER   8
#define UDMA_UPPER  12

   USHORT  PCIReg;
   ULONG   PCIData;
   USHORT  j,k;
   USHORT  DRWValue     =0;
   USHORT  UDMAValue    =0;
   USHORT  UDMATiming   =0;
   USHORT  DRWTIM0_val  =0;
   USHORT  DRWTIM1_val  =0;
   USHORT  DRWTIM2_val  =0;
   USHORT  DRWTIM3_val  =0;
   USHORT  ARTTIM0_val  =0;
   USHORT  ARTTIM1_val  =0;
   USHORT  ARTTIM23_val =0;
   USHORT  CMDTIM0_val  =0;
   USHORT  CMDTIM1_val  =0;
   USHORT  UDIDETCR0_val=0;
   USHORT  UDIDETCR1_val=0;

   /******************************************************/
   /* Allocate a page of physical memory and map it to   */
   /* this ACB's Bus Master Scatter/Gather list pointer. */
   /* Disable Bus Master DMA if this allocation fails.   */
   /******************************************************/
   if( AllocateBMSGL( npACB ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   /*-----------------------*/
   /*    Pre-Adjustments    */
   /*-----------------------*/
   if( CMD_level == CMD648 )
   {
      CkAdj4UDMACableCMD( npACB, npAT );
   }
   else if( CMD_level ==  CMD646 )
   {
      SetUDMA2UDMA33( npACB, PRIMARY_CHANNEL   );           /* Limit 2 UDMA 33 */
      SetUDMA2UDMA33( npACB, SECONDARY_CHANNEL );           
   }

   /*------------------------------------------------------------*/
   /*   Get drive PIO & DMA modes and calculate register values  */
   /*------------------------------------------------------------*/
   for( j=0; j < npACB->cUnits; j++ )
   {
      /*--------------------------*/
      /*         PIO Mode         */
      /*--------------------------*/
      for( k=PIO_LOWER; k<=PIO_UPPER; k++ )
      {
         if( npACB->UnitCB[j].CurPIOMode == CMDRegs[k][0] )  /* Match Drive mode 2 Table mode */
         {
            if( npAT->Channel == PRIMARY_CHANNEL )
            {
               if(!j)                           // Drive 0 Master
               {
                  ARTTIM0_val = CMDRegs[k][1];
                  DRWTIM0_val = CMDRegs[k][3];
               } else {                         // Drive 1 Slave
                  ARTTIM1_val = CMDRegs[k][1];
                  DRWTIM1_val = CMDRegs[k][3];
               }
            } else {
               if( CMDRegs[k][1] > ARTTIM23_val )
               {
                  ARTTIM23_val = CMDRegs[k][1];
               }
               if( !j )                          // Drive 0 Master
               {
                  DRWTIM2_val = CMDRegs[k][3];
               } else {                          // Drive 1 Slave
                  DRWTIM3_val = CMDRegs[k][3];
               }
            }

            if( CMDRegs[k][2] > CMDTIM0_val )
            {
               CMDTIM0_val = CMDRegs[k][2];
            }
         }
      }

      /*-----------------------------------------------------*/
      /*                    UDMA Mode                        */
      /*-----------------------------------------------------*/
      if( npACB->UnitCB[j].UltraDMAMode )
      {
         /*------------------------------------------------*/
         /*     Limit UDMA to ULTRADMAMODE_2 on CMD646     */
         /*------------------------------------------------*/
         if( ( CMD_level == CMD646 ) &&
             (npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 ) )
         {
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_2;
         }

         for( k=UDMA_LOWER; k<=UDMA_UPPER ; k++ )
         {
            if( npACB->UnitCB[j].UltraDMAMode == CMDRegs[k][0] )
            {
               UDMATiming = CMDRegs[k][3];

               if( !j )
               {                                 // Master UDMA
                  UDMAValue  |= UDMATiming;
                  UDMAValue  |= 0x01;
                  if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 )
                  {
                     UDMAValue |= 0x04;          /* UDMA66 Master bit */
                  }
               } else {                          // Slave UDMA
                  UDMATiming <<= 2;
                  UDMAValue   |= UDMATiming;
                  UDMAValue   |= 0x02;
                  if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 )
                  {
                     UDMAValue |= 0x08;          /* UDMA66 Slave bit */
                  }
               }

               if( (npAT->Channel == PRIMARY_CHANNEL)       ||
                   (npAT->Channel == NATIVE_PRIMARY_CHANNEL) )
               {
                  UDIDETCR0_val |= UDMAValue;
               } else {
                  UDIDETCR1_val |= UDMAValue;
               }
            }
         }
      } else {
         /*----------------------------------------------------*/
         /*                    MWDMA Mode                      */
         /*----------------------------------------------------*/
         if( npACB->UnitCB[j].CurDMAMode )
         {
            for( k=DMA_LOWER; k <=DMA_UPPER; k++ )
            {
               if( npACB->UnitCB[j].CurDMAMode == CMDRegs[k][0] )
               {
                  DRWValue = CMDRegs[k][3];
               }
               if( (npAT->Channel == PRIMARY_CHANNEL)       ||
                   (npAT->Channel == NATIVE_PRIMARY_CHANNEL) )
               {
                  if( !j )
                  {
                     DRWTIM0_val = DRWValue;           // Master
                  } else {
                     DRWTIM1_val = DRWValue;           // Slave
                  }
               } else {
                  if( !j )
                  {
                     DRWTIM2_val = DRWValue;           // Master
                  } else {
                     DRWTIM3_val = DRWValue;           // Slave
                  }
               }
            }
         }
      }
   }

   /*--------------------------------*/
   /* Program the CMD chip registers */
   /*--------------------------------*/
   for( j=0; j < 9; j++)
   {
      switch( j )
      {
         case 0:
           PCIReg  = CMDTIM0;                        /* Register */
           PCIData = CMDTIM0_val;                    /* Value    */
           break;
         case 1:
           PCIReg  = ARTTIM0;
           PCIData = ARTTIM0_val;
           break;
         case 2:
           PCIReg  = DRWTIM0;
           PCIData = DRWTIM0_val;
           break;
         case 3:
           PCIReg  = ARTTIM1;
           PCIData = ARTTIM1_val;
           break;
         case 4:
           PCIReg  = DRWTIM1;
           PCIData = DRWTIM1_val;
           break;
         case 5:
           PCIReg  = ARTIM23;
           PCIData = ARTTIM23_val;
           break;
         case 6:
           PCIReg  = DRWTIM2;
           PCIData = DRWTIM2_val;
           break;
         case 7:
           PCIReg  = UDIDETCR0;                    /* UDMA Primary */
           PCIData = UDIDETCR0_val;
           break;
         case 8:
           PCIReg  = UDIDETCR1;                   /* UDMA Secondary */
           PCIData = UDIDETCR1_val;
           break;
         default:
           break;
      }

      /* ----------------------------------------- */
      /* Write out all the configuration registers */
      /* ----------------------------------------- */
      if( WritePCIConfigSpace( &npAT->PCIInfo,
                               PCIReg,
                               PCIData,
                               1             ) )
      {
         DisableAdapterBMDMA( npAT, npACB );
         return( 1 );
      }
   }

   return( 0 );                                           /* Success */
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/                                 
VOID SetIRQ_Shared_bits( USHORT IRQLevel )
{
   if( IRQLevel == 0x0F )                   /* 15 = 0100 0000 0000 0000 */
   {                                        /* 14 = 0010 0000 0000 0000 */
      IRQ_Shared_Bits |= 0x4000;            /*  ; ;   ;    ;    ;   ;   */
   }                                        /*  1 = 0000 0000 0000 0001 */
   if( IRQLevel == 0x0E )
   {
      IRQ_Shared_Bits |= 0x2000;
   }
   return;
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/
USHORT GetIRQSharedBit( USHORT IRQLevel )                           
{
   USHORT i = 1;

   /*--------------------------------------------*/
   /* Native IRQ levels are assumed to be Shared */
   /* All IRQs below 14 are assumed to be Native */
   /*--------------------------------------------*/

   if( IRQLevel < 0x0E )            /* If NATIVE share the IRQ */
   {
      return( IRQ_SHARED );
   } else {

      i <<= ( IRQLevel-1 );         /* Shift the bit by the interrupt level */

      if( IRQ_Shared_Bits & i )     /* Is this bit set?*/
      {
         return( IRQ_SHARED );
      }
   }
   return( IRQ_NOT_SHARED );
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/                                  /*@VXXXXX*/
VOID CkAdj4UDMACableCMD( NPACB npACB, NPATBL npAT )
{
   ULONG  Reg_Data;

   /*-----------------------------------*/
   /* This is the Cable Detection bits  */
   /*-----------------------------------*/
   if( ReadPCIConfigSpace( &npAT->PCIInfo,
                           PCI_CR_BM_BMIDECSR,             /* Cable Detection */
                           &Reg_Data,
                           1              ))
   {
      SetUDMA2UDMA33( npACB, PRIMARY_CHANNEL   );          /* No 80 Pin Cable */
      SetUDMA2UDMA33( npACB, SECONDARY_CHANNEL );
   }
   if( !( Reg_Data & PRIMARY_CHANNEL ) )
   {
      SetUDMA2UDMA33( npACB, PRIMARY_CHANNEL );
   }

   if( !( Reg_Data & SECONDARY_CHANNEL ) )
   {
      SetUDMA2UDMA33( npACB, SECONDARY_CHANNEL );
   }

   return;
}

/*-------------------------------*/
/*                               */
/*                               */
/*-------------------------------*/
VOID SetUDMA2UDMA33( NPACB npACB, UCHAR channel )
{
   UCHAR  UDMA_Limit = ULTRADMAMODE_2;

   if( channel == PRIMARY_CHANNEL )
   {
      if( npACB->UnitCB[0].UltraDMAMode > ULTRADMAMODE_2 )
      {
         npACB->UnitCB[0].UltraDMAMode = UDMA_Limit;
      }
      if( npACB->UnitCB[1].UltraDMAMode > ULTRADMAMODE_2 )
      {
         npACB->UnitCB[1].UltraDMAMode = UDMA_Limit;
      }
   } else {
      if( npACB->UnitCB[2].UltraDMAMode > ULTRADMAMODE_2 )
      {
         npACB->UnitCB[2].UltraDMAMode = UDMA_Limit;
      }
      if( npACB->UnitCB[3].UltraDMAMode > ULTRADMAMODE_2 )
      {
         npACB->UnitCB[3].UltraDMAMode = UDMA_Limit;
      }
   }
}

//start V@253487

/*****************************/
/* Program VIA IDE controller*/
/*****************************/

USHORT ProgramVIAPCI( NPACB npACB, NPATBL npAT, USHORT i, USHORT VIA_Level )
{
   USHORT  j;
   USHORT  SBDeviceID;
   ULONG   Reg_Data;
   USHORT  PCIData;
   UCHAR   PCIDataB;


   /******************************************************/
   /* Allocate a page of physical memory and map it to   */
   /* this ACB's Bus Master Scatter/Gather list pointer. */
   /* Disable Bus Master DMA if this allocation fails.   */
   /******************************************************/
   if( AllocateBMSGL( npACB ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   // Need to find out the chipset revision

   npAT->PCIInfo.DevFunc &= 0xF8;

   if( !(ReadPCIConfigSpace( &npAT->PCIInfo,
                              PCIREG_DEVICE_ID,
                              (PULONG)&SBDeviceID,
                              2                        ) ))
   {
      switch(SBDeviceID)
      {
      case 0x596 : VIA_Level =  VIA596;
         break;
      case 0x686 : VIA_Level =  VIA686;
         break;
      default : VIA_Level =  VIA586;
         break;
      }
   }
   npAT->PCIInfo.DevFunc |= 0x01;

   AdjustVIAUDMA( npACB , VIA_Level);

   CalculateVIATiming( npACB, i);


   if (npACB->PIIX_IDEtim)
   {
      UCHAR AddressSetupTime, Config, FIFOFlush;

      if (!(npACB->PIIX_IDEtim & 0x00FF))
         npACB->PIIX_IDEtim   |= 0x00A8;          /* fill with default timing */
      if (!(npACB->PIIX_IDEtim & 0xFF00))
         npACB->PIIX_IDEtim   |= 0xA800;          /* fill with default timing */

      WritePCIConfigSpace (&npAT->PCIInfo,
                           (UCHAR) (PCI_VIA_IDETIM + ((i == 0) ? 2 : 0)),
                           (ULONG) npACB->PIIX_IDEtim,
                            2);

      ReadPCIConfigSpace (&npAT->PCIInfo,
                          PCI_VIA_IDEASU,
                         (PULONG)&AddressSetupTime,
                          1);
      AddressSetupTime |= (i ? 0x0F : 0xF0);    /* address setup 4 clocks */
      WritePCIConfigSpace (&npAT->PCIInfo,
                           PCI_VIA_IDEASU,
                           AddressSetupTime,
                           1);

      ReadPCIConfigSpace (&npAT->PCIInfo,
                          PCI_VIA_IDEMISC3,
                          (PULONG)&FIFOFlush, 1);

      FIFOFlush |= (i ? 0x40 : 0x80);

      WritePCIConfigSpace (&npAT->PCIInfo,
                           PCI_VIA_IDEMISC3,
                           FIFOFlush,
                           1);

      ReadPCIConfigSpace (&npAT->PCIInfo,
                          PCI_VIA_IDECONFIG,
                          (PULONG)&Config,
                          1);

      Config |= (i ? 0x30 : 0xC0);     /* enable prefetch/post buffers */

      WritePCIConfigSpace (&npAT->PCIInfo,
                           PCI_VIA_IDECONFIG,
                           Config,
                           1);

      ReadPCIConfigSpace  (&npAT->PCIInfo,
                           PCI_VIA_IDEFIFOCFG,
                           (PULONG)&PCIDataB,
                           1);

      PCIDataB = (PCIDataB & 0x8F) | 0x30;

      WritePCIConfigSpace (&npAT->PCIInfo,
                           PCI_VIA_IDEFIFOCFG,
                           PCIDataB,
                           1);
   }


   if (npAT->Flags & ATBF_BM_DMA)
   {    /* if Bus Master DMA on this adapter */
      if (!(npACB->PIIX_SIDEtim & 0x00F7))
         npACB->PIIX_SIDEtim |= 0x0003;     /* fill with default timing */
      if (!(npACB->PIIX_SIDEtim & 0xF700))
         npACB->PIIX_SIDEtim |= 0x0300;     /* fill with default timing */

      ReadPCIConfigSpace (&npAT->PCIInfo,
                          (UCHAR)(PCI_VIA_IDEULTRA + (i ? 0 : 2)),
                          (PULONG)&PCIData,
                          2);

      PCIData |= npACB->PIIX_SIDEtim;

      WritePCIConfigSpace (&npAT->PCIInfo,
                           (UCHAR)(PCI_VIA_IDEULTRA + (i ? 0 : 2)),
                           PCIData,
                           2);

      ReadPCIConfigSpace  (&npAT->PCIInfo,
                           PCI_VIA_IDEMISC2,
                          (PULONG)&PCIDataB,
                           1);

      WritePCIConfigSpace (&npAT->PCIInfo,
                           PCI_VIA_IDEMISC2,
                           PCIDataB | 0x03,
                           1);
   }
   /***********************/
   /* Undocumented by VIA */
   /***********************/

   /*The following code uses undocumented bit, no explanation for it yet.*/

   ReadPCIConfigSpace  (&npAT->PCIInfo,
                        PCI_VIA_IDEMISC2,
                        (PULONG)&PCIDataB,
                        1);
   WritePCIConfigSpace (&npAT->PCIInfo,
                        PCI_VIA_IDEMISC2,
                        PCIDataB | 0x10,
                        1);

   return (0);                  /* Success */

}
/******************************/
/* Program SiS IDE controller */
/******************************/

USHORT ProgramSISPCI( NPACB npACB, NPATBL npAT,USHORT SiS_level, USHORT i)
{
   UCHAR j;
   ULONG k;
   ULONG PCIData;

   /******************************************************/
   /* Allocate a page of physical memory and map it to   */
   /* this ACB's Bus Master Scatter/Gather list pointer. */
   /* Disable Bus Master DMA if this allocation fails.   */
   /******************************************************/
   if( AllocateBMSGL( npACB ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   if( ReadPCIConfigSpace( &npAT->PCIInfo,            // Read Command Register
                            PCIREG_COMMAND,
                           &PCIData,
                           1                        ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   PCIData |= 0x05;

   if( WritePCIConfigSpace( &npAT->PCIInfo,           // Write it
                             PCIREG_COMMAND,
                             PCIData,
                             1             ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }

   //Set interrupt line
   ReadPCIConfigSpace ( &npAT->PCIInfo, PCIREG_INT_LINE, (PULONG)&PCIData, 1);
   WritePCIConfigSpace ( &npAT->PCIInfo, PCIREG_INT_LINE, 0x0F, 1);

   //Set Latency timer
   ReadPCIConfigSpace ( &npAT->PCIInfo, PCIREG_LATENCY, (PULONG)&PCIData, 1);
   if (PCIData < 0x80)
     WritePCIConfigSpace ( &npAT->PCIInfo, PCIREG_LATENCY, 0x80, 1); 

   // Adjust max UDMA
   
   AdjustSiSUDMA(npACB, SiS_level);

  //Check for 80 pin cable on SiS5513

   CheckUDMACableSiS(npACB, npAT, i);


   for (j=0; j< npACB->cUnits;j++)
   {

      if ((npACB->UnitCB[j].UltraDMAMode) && (npACB->UnitCB[j].Flags & UCBF_BM_DMA))
      {
         if( ReadPCIConfigSpace( &npAT->PCIInfo,            // UDMA mode control Register
                                 (PCI_VIA_IDECONFIG+j*2),
                                 &PCIData,
                                 1                        ) )
         {
            DisableAdapterBMDMA( npAT, npACB );
            return( 1 );
         }

         PCIData |= 0x80;

         if( WritePCIConfigSpace( &npAT->PCIInfo,           // Write it
                                  (PCI_VIA_IDECONFIG+j*2),
                                  PCIData,
                                  1             ) )
         {
            DisableAdapterBMDMA( npAT, npACB );
            return( 1 );
         }
      }
   }

 if( ReadPCIConfigSpace( &npAT->PCIInfo,            // Read General Control Register
                           PCI_SIS_IDEGENCTRL,
                           &PCIData,
                           1                        ) )
   {
      DisableAdapterBMDMA( npAT, npACB );
      return( 1 );
   }                    
   return (0);                   /* Success */     
}

/*------------------------*/
/*      AdjustVIAUDMA     */
/*------------------------*/

VOID AdjustVIAUDMA( NPACB npACB, USHORT VIA_level )
{
   USHORT j;

   for ( j=0; j < npACB->cUnits; j++ )
   {
      /*-----------------------------------------------------*/
      /*                    UDMA Mode                        */
      /*-----------------------------------------------------*/
      if( npACB->UnitCB[j].UltraDMAMode )
      {
         /*------------------------------------------------*/
         /*     Limit UDMA mode if needed                  */
         /*------------------------------------------------*/
         if( (( VIA_level == VIA596 ) || (VIA_level == VIA686)) &&
             (npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_4 ) )
         {
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_4;
         }
         else if ((VIA_level == VIA586) &&
                  (npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_2 ))
         {
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_2;
         }
      }
   }

}

/*-------------------------------*/
/*       CheckUDMACableSiS       */
/* Check for 80 pin cable on SiS */
/*-------------------------------*/

VOID CheckUDMACableSiS(NPACB npACB, NPATBL npAT, USHORT j)
{
   USHORT i;
   USHORT force_udma2;
   USHORT CableType;

   ReadPCIConfigSpace( &npAT->PCIInfo,
                       PCI_VIA_IDETIM,              //SiS Cable detection byte
                       (PULONG)&CableType,
                       1);
   
   for (i=0; i < 2; i++)
   {
      force_udma2 = ((CableType >>(4+i)) & 0x01);
      if (force_udma2)
      {
         if (!i)
         {
            SetUDMA2UDMA33(npACB,PRIMARY_CHANNEL);
         }
         else if (j)
         {
            DisableAdapterBMDMA( npAT, npACB );                        
         }                                                             /*must force PIO mode on secondary*/
      }
   }

}



VOID CalculateVIATiming(NPACB npACB, USHORT i)
{
   USHORT j,Temp;

   for (j=i*2; j < (2+2*i); j++)
   {
      DeviceTimingMode( j ,npACB );

      SetVIATimings(i,j,npACB);
   }
}

VOID SetVIATimings(USHORT i, USHORT j ,NPACB npACB)
{

  USHORT UDMA, PIO, Data;
  USHORT PCIClockIndex=0;
  NPUCB  npUCB = &(npACB->UnitCB[j]);

  UCHAR VIA_ModeSets[4][3] =
  {
     { 0xA8, 0x22, 0x20 },   /* 30/33 Mhz PCI clock timings */
     { 0xA8, 0x21, 0x10 },   /* 25 Mhz PCI clock timings */
     { 0xC9, 0x32, 0x21 },   /* 37 Mhz PCI clock timings */
     { 0xDA, 0x33, 0x21 }         /* 41 Mhz PCI clock timings */
  };

  UCHAR VIA_UltraModeSets[4][6] =
  {
     { 0xC2, 0xC1, 0xC0, 0xC4, 0xC5, 0xC6 },   /* 30/33 Mhz PCI clock timings */
     { 0xC1, 0xC1, 0xC0, 0xC4, 0xC5, 0xC6 },   /* 25 Mhz PCI clock timings */
     { 0xC3, 0xC2, 0xC1, 0xC0, 0xC4, 0xC5 },   /* 37 Mhz PCI clock timings */
     { 0xC3, 0xC2, 0xC1, 0xC0, 0xC4, 0xC5 }    /* 41 Mhz PCI clock timings */
  };


  UCHAR VIA_Ultra66ModeSets[4][5] =
  {
     { 0xC6, 0xC4, 0xC2, 0xC1, 0xC0 },   /* 30/33 Mhz PCI clock timings */
     { 0xC4, 0xC3, 0xC1, 0xC1, 0xC0 },   /* 25 Mhz PCI clock timings */
     { 0xC7, 0xC5, 0xC3, 0xC2, 0xC1 },   /* 37 Mhz PCI clock timings */
     { 0xC7, 0xC6, 0xC3, 0xC2, 0xC1 }    /* 41 Mhz PCI clock timings */
  };

  i*=8;

  PIO = npUCB->InterfaceTiming;
  npACB->PIIX_IDEtim |= (USHORT)VIA_ModeSets[PCIClockIndex][PIO] << (i);

  if (npUCB->UltraDMAMode)
  {
     switch (npUCB->UltraDMAMode)
     {
     case ULTRADMAMODE_1: UDMA = 0;
        break;
     case ULTRADMAMODE_2: UDMA = 1;
        break;
     case ULTRADMAMODE_3: UDMA = 2;
        break;
     case ULTRADMAMODE_4: UDMA = 3;
        break;
     default: break;
     }
  }

  if ((npUCB->Flags & UCBF_BM_DMA) && (UDMA > 0) )
  {
    UDMA -= 1;
    if (npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_3 )
      Data = VIA_Ultra66ModeSets[PCIClockIndex][UDMA];
    else
      Data = VIA_UltraModeSets[PCIClockIndex][UDMA];

    npACB->PIIX_SIDEtim = (npACB->PIIX_SIDEtim & (0xFF << i))| (Data << (8 - i));

    if (npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_3 ) npACB->PIIX_SIDEtim |= 0x0008;
  }
}

/*************************************/
/* Adjust max UDMA level for SiS5513 */
/* Note: SiS_Level is for future adds*/
/*************************************/

VOID AdjustSiSUDMA(NPACB npACB, USHORT SiS_Level)
{
   USHORT j;

   for ( j=0; j < npACB->cUnits; j++ )
   {
      /*-----------------------------------------------------*/
      /*                    UDMA Mode                        */
      /*-----------------------------------------------------*/
      if( npACB->UnitCB[j].UltraDMAMode )
      {
         /*------------------------------------------------*/
         /*     Limit UDMA mode if needed                  */
         /*------------------------------------------------*/
         if( npACB->UnitCB[j].UltraDMAMode > ULTRADMAMODE_4 )
         {
            npACB->UnitCB[j].UltraDMAMode = ULTRADMAMODE_4;
         }
      }
   }

}
//end V@253487

