/**************************************************************************
 *
 * 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 AcceptJM (NPA npA)
{
  NPC npC = npA->npC;
  UCHAR combined;

  /* AHCI mode is not supported yet! */
  if (GetRegB (PAdr, PCIREG_SUBCLASS) != PCI_IDE_CONTROLLER) return (FALSE);

  switch (PciInfo->Level) {
    case 0 : npC->numChannels = 1;
    default: npC->numChannels = 2;
  }
goto JMexit;
  combined = GetRegB (PAdr, 0x42) & 0x80;

  if (combined) {  // combined, primary SATA, secondary PATA
    npA->maxUnits = 2;
    if (npA->IDEChannel > 0) goto JMexit; // PATA channel
  } else {
    npA->maxUnits = 1;
  }

  npA->SCR.Offsets = 0x3120;
  GenericSATA (npA);
  npA->UnitCB[0].SStatus = GetAHCISCR (npA, npA->IDEChannel + 0);
  if (npA->maxUnits > 1)
    npA->UnitCB[1].SStatus = GetAHCISCR (npA, npA->IDEChannel + 1);

JMexit:
  npA->FlagsT |= ATBF_BIOSDEFAULTS;
  sprntf (npA->PCIDeviceMsg, JMMsgtxt, MEMBER(npA).Device & 0xFFF);
  return (TRUE);
}

BOOL NEAR AcceptMarvell (NPA npA)
{
  npA->Cap |= CHANCAP_SPEED | CHANCAP_CABLE80;
  npA->FlagsT |= ATBF_BIOSDEFAULTS;
  npA->maxUnits = 2;
  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;
  ULONG  Port;
  NPCH	 p;

  IOMask = npA->IOPendingMaskSave = npA->IOPendingMask;
  npA->IOPendingMask = 0;
  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 (SECTORREG, LBA3); } // LBA3
    if (IOMask & FM_LBA4   ) { outpdelay (CYLLREG  , 0);    } // LBA4
    if (IOMask & FM_LBA5   ) { outpdelay (CYLHREG  , 0);    } // LBA5
  }
  p = &FEAT;
  IOMask &= FM_LOW & ~FM_PCMD;
  for (Port = FI_PFEAT; IOMask; Port++) {
    IOMask >>= 1;
    if (IOMask & 1) outpdelay (npA->IOPorts[Port], *p);
    p++;
  }
}

/*--------------------------------------------*/
/* 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)));
}

