/**************************************************************************
 *
 * SOURCE FILE NAME = S506GNRC.C
 *
 * DESCRIPTIVE NAME = DANIS506.ADD - Adapter Driver for PATA/SATA DASD
 *
 * Copyright : COPYRIGHT Daniela Engert 1999-2006
 *
 * DESCRIPTION : Adapter Driver generic busmaster routines.
 *
 ****************************************************************************/

#define INCL_NOPMAPI
#define INCL_DOSINFOSEG
#define INCL_NO_SCB
#define INCL_INITRP_ONLY
#define INCL_DOSERRORS
#include "os2.h"
#include "dskinit.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 "Trace.h"

#pragma optimize(OPTIMIZE, on)

#define PciInfo (&npA->PCIInfo)
#define PAdr PciInfo->PCIAddr

BOOL NEAR AcceptGeneric (NPA npA)
{
  if ((GenericBM > 0) && npA->FlagsI.b.busmaster && (npA->BMICOM & 0xFFF0)) {
    USHORT Port;
    UCHAR  err = FALSE;

    Port = npA->BMICOM;
    outp (Port, 0);
    if (0 != inp (Port)) err = TRUE;

    Port += 4;
    outp (Port, 0x54);	// bits 0 and 1 are zero!
    if (0x54 != inp (Port)) err = TRUE;

    Port += 2;
    outp (Port, 0xAA);
    if (0xAA != inp (Port)) err = TRUE;

    if (err) {
      TTYWrite (2, "Generic: busmaster registers not available");
      npA->Cap &= ~(CHIPCAP_ATADMA | CHIPCAP_ATAPIDMA);
    } else {
      if (npA->FlagsI.b.native) METHOD(npA).CheckIRQ = BMCheckIRQ;
    }
  } else {
    npA->Cap &= ~(CHIPCAP_ATADMA | CHIPCAP_ATAPIDMA);
  }

  npA->Cap &= ~CHIPCAP_PIO32;
  npA->IRQDelay = -1; // delayed interrupt
  npA->FlagsT |= ATBF_BIOSDEFAULTS;
  return (TRUE);
}

// ---------------------------------------------------------------

BOOL NEAR AcceptNetCell (NPA npA)
{
  npA->Cap &= ~(CHIPCAP_ATAPIPIO32 | CHIPCAP_ATAPIDMA);
  npA->Cap |= CHANCAP_SPEED | CHANCAP_CABLE80 | CHIPCAP_SATA;
  npA->FlagsT |= ATBF_BIOSDEFAULTS;
  npA->maxUnits = 2;
  return (TRUE);
}

// ---------------------------------------------------------------

BOOL NEAR AcceptMarvell (NPA npA)
{
  NPC	npC  = npA->npC;
//  ULONG BAR5 = npC->BAR[5].Addr;

  npA->FlagsT |= ATBF_BIOSDEFAULTS;
  npA->Cap    &= ~(CHIPCAP_ATAPIDMA | CHIPCAP_PIO32);
  npA->maxUnits = 2;

  switch (PciInfo->Level) {
    case 0 :
      npC->numChannels = 1; // 6101
      break;

    default:
      npC->numChannels = 2;
      if (0 == npA->IDEChannel) {  // PATA
//	  if (BAR5 && !(InD (BAR5 | 0x0C) & 0x10))  // PATA enabled
      } else {
	GenericSATA (npA);
      }
      break;
  }

  if (!(npA->Cap & CHIPCAP_SATA)) {
    if (!(InB (BMCMDREG + 1) & 1))
      npA->Cap |= CHANCAP_SPEED | CHANCAP_CABLE80;
  }

  return (TRUE);
}

// ---------------------------------------------------------------

#define AHCI_GHC 4
#define AHCI_GHC_AHCIENABLED 1

BOOL NEAR AcceptAHCI (NPA npA)
{
  NPC	npC  = npA->npC;
  ULONG BAR5 = npC->BAR[5].Addr;

  // AHCI mode enabled?
  if (BAR5 && (InD (BAR5 | AHCI_GHC) & AHCI_GHC_AHCIENABLED)) return (FALSE);

  npA->FlagsT |= ATBF_BIOSDEFAULTS;

  npA->maxUnits = 2;
  npA->SCR.Offsets = 0x3120;
  GenericSATA (npA);
  npA->Cap |= CHIPCAP_ATAPIDMA;
  npA->UnitCB[0].SStatus = GetAHCISCR (npA, npA->IDEChannel * 2 + 0);
  npA->UnitCB[1].SStatus = GetAHCISCR (npA, npA->IDEChannel * 2 + 1);

  return (TRUE);
}

// ---------------------------------------------------------------

USHORT NEAR GetGenericPio (NPA npA, UCHAR Unit) {
  NPU npU = &(npA->UnitCB[Unit]);

  switch (npU->BestPIOMode) {
    case 4: return (4); break;
    case 3: return (6); break;
    default: return (20); break;
  }
}

/*--------------------------------------------*/
/* CalculateGenericAdapterTiming	      */
/* ----------------------		      */
/*					      */
/* Arguments:				      */
/*	npA = nptr to Adapter Control Block   */
/*					      */
/* Actions:				      */
/*					      */
/*	Picks optimal settings for PIO/DMA    */
/*	transfer for the selected ACB.	      */
/*					      */
/* Returns:				      */
/*	Nothing 			      */
/*					      */
/*					      */
/*--------------------------------------------*/

VOID CalculateGenericAdapterTiming (NPA npA)
{
  NPU npU;

  for (npU = npA->UnitCB; npU < (npA->UnitCB + MAX_UNITS); npU++) { /* For each attached unit */
    if (npU->Flags & UCBF_NOTPRESENT) continue;

    npU->CurPIOMode = npU->FoundPIOMode;

    if ((npA->Cap & CHIPCAP_SATA) ||
       (BMSTATUSREG && (InB (BMSTATUSREG) & (npU == npA->UnitCB ? 0x20 : 0x40)))) {
      if (npU->CurDMAMode > 0)	 npU->CurPIOMode = npU->CurDMAMode + 2;
      if (npU->UltraDMAMode > 0) npU->CurDMAMode = 3 + (npU->UltraDMAMode - 1);
    } else {
      npU->CurDMAMode = npU->UltraDMAMode = 0;
      npU->Flags &= ~UCBF_BM_DMA;
    }
  }
}

/*--------------------------------------------*/
/* GenericInitComplete			      */
/*--------------------------------------------*/

VOID NEAR GenericInitComplete (NPA npA)
{
  if (InB (npA->BMISTA) & 0x80) {  /* busmaster supports simplex mode only */
				   /* serialize access to IDE chip	   */
    if (npA->IDEChannel) {
      NPA mate;
      NPHWRESOURCE npHWR;

      mate = npA->npC->npA[1 - npA->IDEChannel];
      if (!mate) return;
      npHWR = mate->npHWR;

      DISABLE
      npA->npHWR->npFirstACBX = 0;
      npHWR->npFirstACBX->npNextACBX = npA;
      npA->npNextACBX = 0;
      npA->npHWR = npHWR;
      ENABLE
    }
  }
}
															/* vvv, @V151345 */
/*----------------------------------------------------*/
/* CheckIRQ					      */
/*						      */
/* return TRUE	if interrupt request is from us       */
/* return FALSE if interrupt request is *not* from us */
/*----------------------------------------------------*/

int NEAR NonsharedCheckIRQ (NPA npA) {
  UCHAR Loop;

  if (!(npA->FlagsT & ATBF_LATE_INT)) {
    if (BMCMDREG && (InB (BMCMDREG) & BMICOM_START)) {
      IODly (npA->IRQDelayCount);
      for (Loop = 256; --Loop != 0;) {
	if ((BMSTATUS = InB (BMSTATUSREG)) & BMISTA_INTERRUPT) {
	  npA->Flags |= ACBF_BMINT_SEEN;
//	    if (!(BMSTATUS & BMISTA_ACTIVE))
	    OutB (BMCMDREG, npA->BM_CommandCode &= npA->BM_StopMask); /* turn OFF Start bit */
//	    else
//	      npA->npU->DeviceCounters.TotalBMStatus2++;
	  OutB (BMSTATUSREG, (UCHAR)(BMSTATUS & BMISTA_MASK));
	  break;
	}
	npA->npU->DeviceCounters.TotalChipStatus++;
	P4_REP_NOP
      }
    }
    STATUS = InB (STATUSREG);
    ENABLE
  }
  return (1);
}


int NEAR BMCheckIRQ (NPA npA) {
  IODly (npA->IRQDelayCount);
  DISABLE
  BMSTATUS = InB (BMSTATUSREG);
  if (BMSTATUS & BMISTA_INTERRUPT) {	      // interrupt is signalled pending
    if (InB (BMCMDREG) & BMICOM_START) {
      OutB (BMCMDREG, npA->BM_CommandCode &= ~BMICOM_START); /* turn OFF Start bit */
    }
    STATUS = InB (STATUSREG);	// Artop: status available after stopping BM !
    npA->Flags |= ACBF_BMINT_SEEN;
    OutB (BMSTATUSREG, (UCHAR)(BMSTATUS & BMISTA_MASK));
    ENABLE
    return (1);
  } else {
    ENABLE
    return (0);
  }
}

/*--------------------------------------------*/
/* GenericSetupTaskFile 		      */
/*--------------------------------------------*/

#define outpdelay(Port,Data) OutBd (Port,Data,npA->IODelayCount)

VOID NEAR GenericSetupTF (NPA npA)
{
  USHORT IOMask = npA->IOPendingMaskSave = npA->IOPendingMask;
  if (IOMask & FM_HIGH) {  // LBA48 addressing
    if (IOMask & FM_HFEAT  ) { outpdelay (FEATREG  , 0);    } // FEAT	H
    if (IOMask & FM_HSECCNT) { outpdelay (SECCNTREG, 0);    } // SECCNT H
    if (IOMask & FM_LBA3   ) { outpdelay (LBA3REG,   LBA3); } // LBA3
    if (IOMask & FM_LBA4   ) { outpdelay (LBA4REG,   LBA4); } // LBA4
    if (IOMask & FM_LBA5   ) { outpdelay (LBA5REG,   LBA5); } // LBA5
  }
    if (IOMask & FM_PFEAT  ) { outpdelay (FEATREG  , FEAT); } // FEAT
    if (IOMask & FM_PSECCNT) { outpdelay (SECCNTREG, SECCNT);} // SECCNT
    if (IOMask & FM_LBA0   ) { outpdelay (LBA0REG,   LBA0); } // LBA0
    if (IOMask & FM_LBA1   ) { outpdelay (LBA1REG,   LBA1); } // LBA1
    if (IOMask & FM_LBA2   ) { outpdelay (LBA2REG,   LBA2); } // LBA2
}

/*--------------------------------------------*/
/* GenericStartOperation		      */
/*--------------------------------------------*/

VOID NEAR GenericStartOp (NPA npA)
{
  if (npA->BM_CommandCode & BMICOM_START) {
    OutB (BMCMDREG, (UCHAR)(npA->BM_CommandCode & ~BMICOM_START));
    OutB (COMMANDREG, COMMAND);
    OutB (BMCMDREG, npA->BM_CommandCode); /* Start BM controller */
  } else {
    OutB (COMMANDREG, COMMAND);
  }
}

/*--------------------------------------------*/
/* GenericStopDMA			      */
/*--------------------------------------------*/

VOID NEAR GenericStopDMA (NPA npA)
{
  if (npA->BM_CommandCode) {
    /*****************************************/
    /* Note: Important to write out correct  */
    /* direction bit per Intel		     */
    /* Dani: ALI needs to be stopped by zero!*/
    /*****************************************/

    OutB (BMCMDREG, (UCHAR)(npA->BM_CommandCode & npA->BM_StopMask)); /* turn OFF Start bit */
    npA->BM_CommandCode = 0;
  }
}

/*--------------------------------------------*/
/* GenericErrorDMA			      */
/*--------------------------------------------*/

VOID NEAR GenericErrorDMA (NPA npA)
{
  OutB (BMSTATUSREG, (UCHAR)(BMISTA_ERROR |   /* clear Error Bit       */
			     npA->BMStatus)); /* clear INTR flag       */
}

/*--------------------------------------------*/
/* GenericSATA				      */
/*--------------------------------------------*/

VOID NEAR GenericSATA (NPA npA)
{
  npA->Cap |= CHIPCAP_SATA;
  npA->Cap &= ~(CHIPCAP_ATAPIDMA);
  METHOD(npA).GetPIOMode = NULL;
  METHOD(npA).Setup	 = NULL;
  MEMBER(npA).CfgTable	 = CfgGeneric;
}

/*--------------------------------------------*/
/* GetAHCISCR				      */
/*--------------------------------------------*/

#define AHCI_CAP     0
#define AHCI_PI   0x0C
#define AHCI_SSTS 0x28

ULONG NEAR GetAHCISCR (NPA npA, USHORT Port)
{
  ULONG BAR5 = npA->npC->BAR[5].Addr;
  ULONG CAP;

  if (!BAR5 || (Port >= 32)) return 0;

  CAP = InD (BAR5 | AHCI_CAP);
  /* if possible, check physical presence of port */
  if (CAP && ~CAP) {
    UCHAR numPorts = CAP & 0x1F;
    ULONG PI = InD (BAR5 | AHCI_PI);

    if (Port > numPorts) return 0;
    if (!((PI >> Port) & 1)) return 0;
  }

  return (BAR5 + (0x100 + AHCI_SSTS + (Port * 0x80)));
}

